Styling introduction

Mikle edited this page Aug 26, 2016 · 4 revisions

Available since: WebLaF v1.2.9 release
Required Java version: Java 6 update 30 or any later
Module: ui


Introduction

Starting with v1.29 update WebLaF provides proper styling support for each Swing and extended WebLaF component. New styling system allows you to style any component within your application using predefined skins and styles or custom ones which you can easily define on your own.

Styling system consists of three key elements:

  • Skins
  • Styles
  • Painters

To fully understand how this system works you need to understand each of these key parts, so first of all I will shortly explain how they function. Links for extensive guide on each specific part will also be provided at the end of each section.

I will also explain how StyleManager functions and provide some practical examples for styles and skins usage with explanations and illustrations.

Skins

Skins exist to groups multiple separate component styles and provide convenient mechanisms to access styles for the L&F. It also contains additional general information like skin icon, title, description, author and some other which might be useful when providing skin selection within your application.

Skins can be provided through the code, but the most convenient way is to define them separately in XML format. Here is a small example based on default WebSkin how does single skin look like in XML:

<skin>
    <id>weblaf.default.skin</id>
    <title>Default skin</title>
    <description>Default WebLaF interface skin</description>
    <author>Mikle Garin</author>
    <supportedSystems>all</supportedSystems>
    <class>com.alee.managers.style.skin.web.WebSkin</class>

    <!-- Styles and includes can be provided here -->

</skin>

Each actual skin must implement com.alee.managers.style.skin.Skin interface, but there are ready-to-use AbstractSkin and CustomSkin classes available in the WebLaF ui module which provide partial and full implementations for Skin interface.

AbstractSkin has basic implementation for a few common methods, but you will still have to provide styles for each existing components if you are using it. This class might be useful if you want to provide a separate way of defining skins (maybe through CSS?) apart from XML.

CustomSkin unlike AbstractSkin is a complete Skin implementation that provides styles based on skin XML description. XML description location can be provided when CustomSkin is constructed. Nothing else is actually needed to make it work properly.

Styles

Styles provide specific settings for the components they are written for. Component to which style is applied is defined by its type and id settings. Default components styles have equal type and id.

Each style is attached to the actual components in the UI by default, but you can change the assigned style in runtime. To do that you will need to provide StyleId instance referencing specific style into the Styleable component directly or through StyleManager.setStyleId ( JComponent component, StyleId id ) method.

Single style data is represented by com.alee.managers.style.data.ComponentStyle class in the code and designed mostly for internal usage within skin implementation and StyleManager.

Styles can be provided through the code, but the most convenient way is to define them within skin XML files. Here is a small example based on default WebSkin how does single style look like in XML:

<skin>
    <style type="label">
        <component>
            <opaque>false</opaque>
            <foreground>black</foreground>
            <background>237,237,237</background>
        </component>
        <painter class="WebLabelPainter">
            <drawShade>false</drawShade>
            <shadeColor>210,210,210</shadeColor>
            <shadeSize>2</shadeSize>
            <rotation>none</rotation>
        </painter>
    </style>
</skin>

This part is taken from label.xml file included in WebSkin and represents default style used for JLabel component. It defines JLabel and WebLabelPainter specific settings. Each setting is basically a field inside those classes which value will be set upon installation of this style along with the skin it will be included into.

Each style can also contain ui block to define component UI settings in a similar way they are defined for component and its painter. Also you can create sub-styles within other styles to create structures which you can later use within your complex UIs through linking component to its parent through StyleId you provide.

Painters

Painters are the core element of new styling system - they provide all the painting code for each specific component and are highly customizable. They were initially designed to simplify some of the painting operations and provide developers with additional tools to replace painting code parts with custom ones. For the v1.29 update they were slightly redesigned and tuned to allow full component view customization.

Each existing painter implements com.alee.painter.Painter interface and in addition to that component specific painters also implement their own interfaces like ButtonPainter or TabbedPanePainter.

Making all painters interface-based instead of UIs which are class-based (remember ComponentUI is an abstract class and each specific UI like ButtonUI are also classes, mostly empty ones) allowed me to provide single complex painting implementation for multiple visually similar components at once. For example take a look at WebDecorationPainter class - it is designed for any kind of components and provides a huge amount of possible settings which will only increase with time providing more customization options. And all those options are automatically available in each component using this painter as a base, for example WebButtonPainter or WebToolBarPainter.

Also painters are the most important part of styles and like the styles they can be created through the code and can also be defined through XML as a part of style. You have already seen in the example above how does label painter definition look like, so I'll give you another example:

<skin>
    <style type="separator">
        <painter class="WebSeparatorPainter">
            <lines>
                <line>
                    <color fraction="0.0">255,255,255,5</color>
                    <color fraction="0.5">white</color>
                    <color fraction="1.0">255,255,255,5</color>
                </line>
                <line>
                    <color fraction="0.0">176,182,188,5</color>
                    <color fraction="0.5">176,182,188</color>
                    <color fraction="1.0">176,182,188,5</color>
                </line>
                <line>
                    <color fraction="0.0">255,255,255,5</color>
                    <color fraction="0.5">white</color>
                    <color fraction="1.0">255,255,255,5</color>
                </line>
            </lines>
        </painter>
    </style>
</skin>

This part is taken from separator.xml file included in WebSkin and represents default style used for JSeparator component. It was already refined to the level I want a lot all painters to be at - it should be as agile as it could be, but also simple, convenient and readable at the same time.

It isn't hard to understand by looking at this specific painter how exactly will the final separator look like and this is generally the final goal I will working towards by improving existing painters.

StyleManager

To bring structured styling system to life skins, styles and painters are stored and managed within StyleManager. It provides all possible methods to work with skins, allows you to style separate components and listen to various style events.

StyleManager doesn't really do much on its own, though it handles creation and storing of StyleData class instances for each component that was actually styled. That helper class stores various runtime data on the component styling which is not coming from styles:

  • skin - Skin instance which currently used for the component
  • styleId - StyleId instance applied to that component
  • painters - Custom component painters provided from the code
  • children - Style children linked through StyleId parent field
  • listeners - Component style listeners

It might also store some additional data in future with improvements to the styling system, for example it might start keeping custom settings provided for that component. Though this is just an enhancement idea right now.

Also all interactions with StyleData are performed by StyleManager itself and it shouldn't be accessed from outside to avoid styling issues.

Styling in practice

Now when you have a base knowledge about key elements involved in styling process its time to get the actual examples of how that knowledge could be applied.

As already mentioned before - you need to apply StyleId to your components to modify their styling in runtime, otherwise they will be using the globally applied skin which is WebSkin by default in WebLaF. So let's try and style some small group of components to see how it works.

First of all, this is what we have by default:

public class Example
{
    public static void main ( final String[] args )
    {
        WebLookAndFeel.install ();

        final WebPanel panel = new WebPanel ( new VerticalFlowLayout ( true, true ) );

        final WebLabel title = new WebLabel ( "Panel Title" );
        panel.add ( title );

        final WebSeparator separator = new WebSeparator ();
        panel.add ( separator );

        final WebTextArea textArea = new WebTextArea ( 3, 20 );
        final WebScrollPane scrollPane = new WebScrollPane ( textArea );
        panel.add ( scrollPane );

        TestFrame.show ( panel );
    }
}

Just a note - I am using Web* components instead of J* components here for convenience. You can do all the same I will be doing in this example using methods StyleManager provides. Web* components simply have all those methods included for the usage simplicity.

Here is the result:

Not really appealing, right? Let's do something about it using the predefined styles:

public class Example
{
    public static void main ( final String[] args )
    {
        WebLookAndFeel.install ();

        final WebPanel panel = new WebPanel ( StyleId.panelDecorated, new VerticalFlowLayout ( true, true ) );

        final WebLabel title = new WebLabel ( StyleId.labelShade, "Panel Title" );
        panel.add ( title );

        final WebSeparator separator = new WebSeparator ( StyleId.separatorHorizontal );
        panel.add ( separator );

        final WebTextArea textArea = new WebTextArea ( 3, 20 );
        final WebScrollPane scrollPane = new WebScrollPane ( StyleId.scrollpaneUndecorated, textArea );
        panel.add ( scrollPane );

        TestFrame.show ( panel );
    }
}

We simply applied a few common predefined styles available in the StyleId class and here is the result we get:

Much better. But still something is not right - probably we want to have some spacing between the label and the panel/separator and the panel gradient background isn't really doing a good job here either.

And we actually don't have any guideline for this part of our UI which might be fine if it is pretty small, but it is not something you want to overlook in applications with lots of UI elements.

So because of that and since we didn't find some styles we need - let's write them!

First of all we need to define our skin in XML:

<skin>

    <!-- This is the unique ID of our skin -->
    <id>example.skin</id>

    <!-- And its title, whatever you like it to be -->
    <title>Example skin</title>

    <!-- Short description if you want to have any -->
    <description>This is an example skin for WebLaF wiki</description>

    <!-- And all the glory goes to... -->
    <author>Mikle Garin</author>

    <!-- Let's support all platforms, why not -->
    <supportedSystems>all</supportedSystems>

    <!-- This is our base skin class -->
    <!-- It is used to find relative resources like includes and painter classes -->
    <class>com.alee.managers.style.skin.web.WebSkin</class>

    <!-- Including WebLaF default skin -->
    <!-- This will generally include all default styles into our custom skin -->
    <include>resources/skin.xml</include>

    <!-- Additional styles used for our customized panel -->
    <include nearClass="com.alee.wiki.introduction.Example">ExamplePanel.xml</include>

</skin>

We are using default WebSkin as a base for our customization as it would take a lot of time to create completely new skin from a scratch. Plus it generally works for us, we are just tuning some UI parts.

Note that ExamplePanel.xml include additionally specifies nearClass setting to properly point at the skin file location, otherwise styling system will try to find that skin file near base class which is WebSkin

We also need to setup that skin in our application:

StyleManager.setSkin ( new CustomSkin ( Example.class, "ExampleSkin.xml" ) );

Now we have the skin definition and have also included another skin where we are going to place custom skins. Let's move on to the ExamplePanel.xml skin and write the panel style first:

<skin>

    <!-- Our custom panel style -->
    <style type="panel" extends="decorated">
        <painter class="WebPanelPainter">
            <round>5</round>
            <shadeWidth>15</shadeWidth>
            <webColoredBackground>false</webColoredBackground>
        </painter>
    </style>

</skin>

Great! Let's run this... and something went totally wrong there:

That happened because we just applied the same decorated style for all panels in our application - content pane is a panel and TestFrame uses another panel to store its content. This is why two additional panels with huge shade appeared.

So, let's fix this by applying a custom ID for our panel style:

<skin>

    <!-- Our custom panel style -->
    <style type="panel" id="shaded" extends="decorated">
        <painter class="WebPanelPainter">
            <round>5</round>
            <shadeWidth>15</shadeWidth>
            <webColoredBackground>false</webColoredBackground>
        </painter>
    </style>

</skin>

And we must also reference it properly in the panel now which we also forgot to do before that:

final WebPanel panel = new WebPanel ( StyleId.of ( "shaded" ), new VerticalFlowLayout ( true, true ) );

And here is the desired result:

We should be done with the panel now, so let's move to the other parts. I'll skip all similar steps I've explained for the panel to be short, so here is the resulting style:

<skin>

    <!-- Our custom panel style -->
    <style type="panel" id="shaded" extends="decorated">
        <painter class="WebPanelPainter">
            <round>5</round>
            <shadeWidth>15</shadeWidth>
            <webColoredBackground>false</webColoredBackground>
        </painter>
    </style>

    <!-- Title label style -->
    <style type="label" id="title" extends="shade" padding="5,7,5,7" />

    <!-- Separator style -->
    <style type="separator" id="line" extends="horizontal" />

    <!-- Scroll pane style -->
    <style type="scrollpane" id="scroll" extends="transparent" padding="5,5,5,5" />

    <!-- Text area style -->
    <style type="textarea" id="text">
        <component>
            <opaque>false</opaque>
        </component>
    </style>

</skin>

And the code which references those styles:

public class Example
{
    public static void main ( final String[] args )
    {
        WebLookAndFeel.install ();
        StyleManager.setSkin ( new CustomSkin ( Example.class, "ExampleSkin.xml" ) );

        final WebPanel panel = new WebPanel ( StyleId.of ( "shaded" ), new VerticalFlowLayout ( true, true ) );

        final WebLabel title = new WebLabel ( StyleId.of ( "title" ), "Panel Title" );
        panel.add ( title );

        final WebSeparator separator = new WebSeparator ( StyleId.of ( "line" ) );
        panel.add ( separator );

        final WebTextArea textArea = new WebTextArea ( StyleId.of ( "text" ), 3, 20 );
        final WebScrollPane scrollPane = new WebScrollPane ( StyleId.of ( "scroll" ), textArea );
        scrollPane.setHorizontalScrollBarPolicy ( WebScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
        panel.add ( scrollPane );

        TestFrame.show ( panel );
    }
}

I have added a small fix for the scroll here as well to hide unnecessary horizontal scroll bar.

And this is the resulting view:

Just what we wanted! Look awesome even with some text typed in:

But our style and UI code looks kinda bad with all those hardcoded styles - we should do something about it. Let's structure our style so that it actually reflects the structure of our panel:

<skin>

    <!-- Our custom panel style -->
    <style type="panel" id="shaded" extends="decorated">
        <painter class="WebPanelPainter">
            <round>5</round>
            <shadeWidth>15</shadeWidth>
            <webColoredBackground>false</webColoredBackground>
        </painter>

        <!-- Title label style -->
        <style type="label" id="title" extends="shade" padding="5,7,5,7" />

        <!-- Separator style -->
        <style type="separator" id="line" extends="horizontal" />

        <!-- Scroll pane style -->
        <style type="scrollpane" id="scroll" extends="transparent" padding="5,5,5,5">

            <!-- Text area style -->
            <style type="textarea" id="text">
                <component>
                    <opaque>false</opaque>
                </component>
            </style>

        </style>

    </style>

</skin>

It looks way better now and it also shows how components are placed within the UI.

Now let's have a look at the changes in the code I had to make to apply these styles:

public class Example
{
    public static void main ( final String[] args )
    {
        WebLookAndFeel.install ();
        StyleManager.setSkin ( new CustomSkin ( Example.class, "ExampleSkin.xml" ) );

        final WebPanel panel = new WebPanel ( StyleId.of ( "shaded" ), new VerticalFlowLayout ( true, true ) );

        final WebLabel title = new WebLabel ( StyleId.of ( "title", panel ), "Panel Title" );
        panel.add ( title );

        final WebSeparator separator = new WebSeparator ( StyleId.of ( "line", panel ) );
        panel.add ( separator );

        final WebScrollPane scrollPane = new WebScrollPane ( StyleId.of ( "scroll", panel ) );
        scrollPane.setHorizontalScrollBarPolicy ( WebScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
        scrollPane.getViewport ().setView ( new WebTextArea ( StyleId.of ( "text", scrollPane ), 3, 20 ) );
        panel.add ( scrollPane );

        TestFrame.show ( panel );
    }
}

The main change here - I had to provide parent into StyleId to properly link styles according to their structure in XML. Though you can set full style ID instead of parent reference:

StyleId.of ( "shaded.title" )

can be used instead of:

StyleId.of ( "title", panel )

I generally do not recommend doing that. Specifying parent might look excessive right now, but I will explain why it is important and how it can be really useful.

Note that you don't need to fully reflect components placement in the styles, you don't actually need to reflect it at all. I only recommend grouping styles like this when it is possible for maintainability reasons. On practice it is sometimes hard to reach out for the parent to setup styles like that and might not be needed at all, it depends on the case.

But before we go deeper into that, let's add last few changes to the code - we still didn't do anything about hardcoded style references there.

I came up with a pretty simple solution which might not look too good, but it certainly helps you to avoid problems and gather up style definitions in one place:

public class ExampleStyles
{
    public static final StyleId shaded = StyleId.of ( "shaded" );
    public static final ChildStyleId title = ChildStyleId.of ( "title" );
    public static final ChildStyleId line = ChildStyleId.of ( "line" );
    public static final ChildStyleId scroll = ChildStyleId.of ( "scroll" );
    public static final ChildStyleId text = ChildStyleId.of ( "text" );
}

We have moved all the styles we had into a separate class and can use them anywhere in our application now by simply referencing this class constants.

Also there is a ChildStyleId I have added to conveniently link children styles when you use these constants for specific components. Here is how our panel looks like now:

public class Example
{
    public static void main ( final String[] args )
    {
        WebLookAndFeel.install ();
        StyleManager.setSkin ( new CustomSkin ( Example.class, "ExampleSkin.xml" ) );

        final WebPanel panel = new WebPanel ( ExampleStyles.shaded, new VerticalFlowLayout ( true, true ) );

        final WebLabel title = new WebLabel ( ExampleStyles.title.at ( panel ), "Panel Title" );
        panel.add ( title );

        final WebSeparator separator = new WebSeparator ( ExampleStyles.line.at ( panel ) );
        panel.add ( separator );

        final WebScrollPane scrollPane = new WebScrollPane ( ExampleStyles.scroll.at ( panel ) );
        scrollPane.setHorizontalScrollBarPolicy ( WebScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
        scrollPane.getViewport ().setView ( new WebTextArea ( ExampleStyles.text.at ( scrollPane ), 3, 20 ) );
        panel.add ( scrollPane );

        TestFrame.show ( panel );
    }
}

Better than before, right? And it is still working exactly like before:

Just one more visual thing I would love to fix here - gray background under the text area. Probably I should have used two separate panels for the top and bottom sides and simply style the bottom panel to have white background, but sometimes it might be hard to change the structure of your UI, so we will work with what we have:

<skin>

    <!-- Our custom panel style -->
    <style type="panel" id="shaded" extends="decorated">
        <painter class="WebPanelPainter">
            <round>5</round>
            <shadeWidth>15</shadeWidth>
            <webColoredBackground>false</webColoredBackground>
        </painter>

        <!-- Title label style -->
        <style type="label" id="title" extends="shade" padding="5,7,5,7" />

        <!-- Separator style -->
        <style type="separator" id="line" extends="horizontal" />

        <!-- Scroll pane style -->
        <style type="scrollpane" id="scroll" extends="transparent" padding="5,5,5,5">
            <component>
                <background>white</background>
            </component>
            <painter class="WebScrollPanePainter">
                <undecorated>false</undecorated>
                <round>5</round>
                <shadeWidth>0</shadeWidth>
                <paintBackground>true</paintBackground>
                <webColoredBackground>false</webColoredBackground>
                <paintTop>false</paintTop>
                <borderColor>white</borderColor>
            </painter>

            <!-- Text area style -->
            <style type="textarea" id="text">
                <component>
                    <opaque>false</opaque>
                </component>
            </style>

        </style>

    </style>

</skin>

I have made a small trick there - colored the scrollpane background to white and made it fit the rounding of the panel. I have also disabled its top decoration side to force it having non-rounded corners at the top. And we get exactly the result we wanted:

Now when we are done "playing" with the style and the code - I'll explain why it might be important to provide parent component instead of specifying static style ID path.

First of all I'll write a small modification to our existing panel style in ExamplePanel.xml file:

    <!-- Modification for our custom panel -->
    <style type="panel" id="shaded-large" extends="shaded">
        <painter class="WebPanelPainter">
            <shadeWidth>25</shadeWidth>
        </painter>

        <!-- Scroll pane style -->
        <style type="scrollpane" id="scroll">
            <component>
                <background>255,155,155</background>
            </component>
            <painter class="WebScrollPanePainter">
                <borderColor>255,155,155</borderColor>
            </painter>

            <!-- Custom scrollbar style -->
            <style type="scrollbar" id="scrollbar" extends="undecorated-buttonless" />

        </style>

    </style>

As you can see - it extends the base style we created and changes the shade width for the panel, uses slightly different scrollbar for the scrollpane and sets its background to light red. If we apply it to the panel instead of our base style:

final WebPanel panel = new WebPanel ( ExampleStyles.shadedLarge, new VerticalFlowLayout ( true, true ) );

This is what we will see:

Not too visually appealing, but it demonstrates the difference. Now if we would have statically referenced style within the scrollpane like this:

StyleId.of ( "shaded.scroll" )

we would see this instead:

Because even though the panel style has changed to our second style variation - scrollpane is still referencing the initial first one. That problem doesn't appear if we reference parent component because then the complete style ID is generated in runtime each time component style is updated.

This is the main reason why I recommend to use dynamic style parent reference instead of static style ID and ChildStyleId class was added to make referencing it short and convenient.

Skins in practice

We already made styling for our panel - even two actually - so we will try working with skins now. To start with - let's create the dark skin for our panel - DarkExampleSkin.xml:

<skin>

    <!-- This is the unique ID of our skin -->
    <id>example.skin</id>

    <!-- And its title, whatever you like it to be -->
    <title>Dark Example skin</title>

    <!-- Short description if you want to have any -->
    <description>This is an example dark skin for WebLaF wiki</description>

    <!-- And all the glory goes to... -->
    <author>Mikle Garin</author>

    <!-- Let's support all platforms, why not -->
    <supportedSystems>all</supportedSystems>

    <!-- This is our base skin class -->
    <!-- It is used to find relative resources like includes and painter classes -->
    <class>com.alee.managers.style.skin.dark.DarkWebSkin</class>

    <!-- Including WebLaF dark skin -->
    <!-- This will generally include all dark styles into our custom skin -->
    <include>resources/skin.xml</include>

    <!-- Additional styles used for our customized panel -->
    <include nearClass="com.alee.wiki.introduction.Example">ExamplePanel.xml</include>
    <include nearClass="com.alee.wiki.introduction.Example">DarkExamplePanel.xml</include>

</skin>

We include both of our panel skins here to override the default one in the dark one. That will help us to maintain those skins a lot in future. This might not always be the best way to do things, but it is certainly a good way to do this in case skins don't have a lot of differences.

Here is the DarkExamplePanel.xml style:

<skin>

    <!-- Modification for our custom panel -->
    <style type="panel" id="shaded">
        <component>
            <background>77,81,83</background>
        </component>
        <painter class="WebPanelPainter">
            <borderColor>67,72,73</borderColor>
        </painter>

        <!-- Scroll pane style -->
        <style type="scrollpane" id="scroll">
            <component>
                <background>77,81,83</background>
            </component>
            <painter class="WebScrollPanePainter">
                <borderColor>77,81,83</borderColor>
            </painter>

            <!-- Text area style -->
            <style type="textarea" id="text">
                <component>
                    <foreground>235,235,235</foreground>
                </component>
            </style>

        </style>

    </style>

</skin>

Now we just need to replace the skin we load:

StyleManager.setSkin ( new CustomSkin ( Example.class, "DarkExampleSkin.xml" ) );

and we can see the dark version of our panel:

Pretty simple and straightforward. Of course it can be tweaked more to look even better, but for the last few examples this should be enough so let's stick to these two skins.

We still want to switch between these two styles in runtime, so let's implement that in our small example UI:

public class Example
{
    public static void main ( final String[] args )
    {
        WebLookAndFeel.install ();

        final CustomSkin defaultSkin = new CustomSkin ( Example.class, "ExampleSkin.xml" );
        final CustomSkin darkSkin = new CustomSkin ( Example.class, "DarkExampleSkin.xml" );
        StyleManager.setSkin ( defaultSkin );
        HotkeyManager.registerHotkey ( Hotkey.CTRL_SPACE, new HotkeyRunnable ()
        {
            @Override
            public void run ( final KeyEvent e )
            {
                StyleManager.setSkin ( StyleManager.getSkin () == defaultSkin ? darkSkin : defaultSkin );
            }
        } );

        final WebPanel panel = new WebPanel ( ExampleStyles.shaded, new VerticalFlowLayout ( true, true ) );

        final WebLabel title = new WebLabel ( ExampleStyles.title.at ( panel ), "Panel Title" );
        panel.add ( title );

        final WebSeparator separator = new WebSeparator ( ExampleStyles.line.at ( panel ) );
        panel.add ( separator );

        final WebScrollPane scrollPane = new WebScrollPane ( ExampleStyles.scroll.at ( panel ) );
        scrollPane.setHorizontalScrollBarPolicy ( WebScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
        scrollPane.getViewport ().setView ( new WebTextArea ( ExampleStyles.text.at ( scrollPane ), 3, 20 ) );
        panel.add ( scrollPane );

        TestFrame.show ( panel );
    }
}

And now we can switch the skin using SHIFT+SPACE hotkey:

Since both skins were already initialized at the beginning that switch takes just a tiny amount of time - something around 3~8 ms. Of course if you have a huge application with a lot of initialized UI elements - it will take more time because of the repaints and updates. The actual skin change though will seem almost instant for the user.

One last topic I didn't touch here yet - can you actually use multiply skins at once? - The answer is YES, you certainly can do that. And there are a few thing in WebLaF which will help you with that.

Let's try applying the dark skin to a part of our panel example - the scrollpane:

final WebScrollPane scrollPane = new WebScrollPane ( ExampleStyles.scroll.at ( panel ) );
scrollPane.setHorizontalScrollBarPolicy ( WebScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
scrollPane.getViewport ().setView ( new WebTextArea ( ExampleStyles.text.at ( scrollPane ), 3, 20 ) );
scrollPane.setSkin ( darkSkin );
panel.add ( scrollPane );

Yep, that simple - just set the skin and it will be passed to all component children referenced through the style OR you can call a different method to force recursive updates on all children under that component. And this is how our example will look like now:

I switched the style three times so you can ensure that the black part skin is pinned to dark one and isn't changing upon the global skin change. This was made specifically for cases when you setup a different skin onto some part of the UI.

Available settings

Now when you are almost ready to start styling your application you might ask one last question:

Where should I find all the settings available for each component, UI and painter?

And the answer is simple - look and the source code of the appropriate component, UI or painter class.

Every single setting available in the XML represents a field in the appropriate class. For example the round setting I used a lot for the panel in the example - it comes from WebPanelPainter class which is empty but extends WebContainerPainter and eventually WebDecorationPainter - the latter has that setting.

As an alternative option - you can look at the default skin XML files which already contain styling for all existing components. Those are a good example of the settings you can use.

Right now I am working on the painters and will bring more appropriate description for each single one of them, so you will be able to understand what each specific setting does by just reading a comment near the field.

I was thinking to provide a separate list of available settings, but it will certainly take a lot of time to update it according to all the changes and improvements coming. So I will focus on the changes right now to ensure that release will be on time and that it will be successful. And since WebLaF is fully open-source you can easily attach the sources to your project to have a look at the classes you need to configure in the style XML.

Time to try it!

This is it, you should know enough by now to start creating your own styles and skins using WebLaF!

You can also download source code for the example from this article here:
styling-example.zip

Contact me anytime if something is still missing or feels incomplete - I will update article with more useful information to cover up the blank spots.