Closed husker-dev closed 4 years ago
All styles that are read from XML in runtime are stored in SkinInfo
data objects contained in each separate XmlSkin
or XmlSkinExtension
instance.
SkinInfo
is the main class that contains both raw and compiled ComponentStyle
objects. Raw ones are exactly the barebone styles read from XML files "as is" (except that includes are all compiled into a single list of styles in the order they were provided in XML). Compiled ones contain all information required for the final rendering of components - so they already contain all information from styles they extend or override.
Compillation of loaded raw styles occurs first time someone requests any ComponentStyle
from the SkinInfo
instance, so it has somewhat lazy initialization.
Classes for reference:
I also have some major changes for those classes locally, but mostly unrelated to how styles are loaded. So complexity is not going anywhere yet, just some internal refactoring and improvements.
So, back to the question:
how to apply skin to component without creating .xml file
Unfortunately it isn't possible right now because of the complexity of current implementation - there is no simple way to add more styles on top of loaded ones because of possible errors it might cause (and will cause in some specific cases).
As one of examples - overriding existing style have to force a complete skin & extensions reload, otherwise it will simply not function correctly. There is no "nice" way to do that in the current implementation.
Another example - adding new style requires it to be compiled separately, but using existing styles as dependencies which is currently implemented through extensions which can be loaded in runtime, but this is a one-time thing with no rollback options - once styles with specific identifiers are loaded from the extension and compiled into the main skin they cannot be removed or replaced by another extension. So while that works perfectly fine for extensions - it won't work for a style editor application as you will most probably try loading the same style multiple times and it will be rejected by the skin since it already has style with that identifier.
So as it currently stands - any change in styles in runtime requires at least a full recompile of all styles from the skin and its extensions (which takes a lot of time) and there is no public API available to do it either.
That being said - you might have noticed that there is a working StyleEditor
component/frame available in weblaf-ui
module and it actually allows editing styles in runtime.
That thing is using a hack I've made specifically for StyleEditor
years ago:
Basically this XStream converter written for SkinInfo
class allows providing custom XML text for particular includes ( SkinInfoConverter.addCustomResource ( ... )
) and uses them whenever some skin is loaded. It doesn't check what type of skin is loaded, whether include actually fits it, whether there are any other issues with structure or not etc. It's just a dirty hack for temporary StyleEditor
functioning, nothing more.
So StyleEditor
simply uses that hack for providing edited text of specific includes from the skin in runtime and reloads skin completely to display updates. It is a very slow and inefficient approach and it has a lot of drawbacks, so I didn't update it a lot since it was first added in the library for testing purposes.
I already do have plans for the StyleEditor
for the v1.2.12 update though: #540
This will most probably include revamping the way styles can be loaded and a lot of internal improvements to make styles API actually useful outside of the internal tools, so my general recommendation is to wait until that particular enhancement is out.
I am currently wrapping up some big changes for v1.2.11 and it will most probably be released this or next week and I'll be moving onto v1.2.12 at that point. So while that particular issue (#540) is probably not going to be implemented first, it is something I will be working on in the near future and there I will be able to provide a better API for runtime style manipulations along with it.
TL;DR:
There is no public API to do what you asked currently and there are a lot of issues with implementing it, so it can't be done easily. That being said - I do have plans to add it in near future, specifically for creating a better implementation of StyleEditor
included in WebLaF library, so stay tuned for updates in #540
You wrote that you used it in your StyleEditor
SkinInfoConverter.addCustomResource ( ... )
But what then does it?
SkinInfo skinInfo = XmlUtils.fromXML(text);
StyleManager.setSkin(new XmlSkin(skinInfo));
Why can't I just put skin in XmlUtils.fromXML(text)
?
You can put your skin there for sure, but it needs to be complete in that case. Complete means that it must have ALL styles that each skin have to support to be valid - meaning you need to do either of two things:
Provide ALL styles directly in that text - which is quite unreasonable as there are hundreds of styles that are required by default, you can check StyleId
class to see the full list. Although you don't necessarily need all of them - once you encounter any component that asks non-existing style you will receive an exception.
Have include of existing skin that is available in runtime (like WebLightSkin
) to cover all required styles right away. Then add any custom styles in the very same text, for instance something like this should work (but not guaranteed):
<skin xmlns="http://weblookandfeel.com/XmlSkin">
<!-- Skin settings -->
<id>example.skin</id>
<class>some.existing.Class</class>
<supportedSystems>all</supportedSystems>
<!-- Including WebLaF default skin, will use its style as a base -->
<include nearClass="com.alee.skin.light.WebLightSkin">resources/web-light-skin.xml</include>
<!-- Custom styles here -->
...
First big problem here - you need that `some.existing.Class` to exist in runtime. It can be whatever, but it will be used for locating includes that don't have `nearClass` attributes and for skin icon. That means there is basically no good way to load a multi-file skin from text in runtime without hacking with `SkinInfoConverter.addCustomResource ( ... )` and even then there are some caveats.
So the reason my answer was
there is no public API to do what you asked currently
Isn't because there really isn't any way at all, but because I cannot guarantee it will work properly - most probably it won't and you will encounter some issues along the way, sooner or later.
Basically current API was not really meant to load skins from text, but only from files within your application JAR.
Perhaps this is not very related to the question, but
<include nearClass="com.alee.skin.light.WebLightSkin">
in current WebLaF version should be
<include nearClass="com.alee.skin.web.WebSkin">
.
Also this issue is in Style introduction
in Wiki (https://github.com/mgarin/weblaf/wiki/Styling-introduction)
It's actually already adjusted (in that wiki article) for v1.2.11 as the update is coming this week and WebLightSkin
is already available in SNAPSHOT builds.
Was doing it in advance as I was pushing some big changes recently and writing this article about IconManager
: https://github.com/mgarin/weblaf/wiki/How-to-use-IconManager
Otherwise I'm pretty sure I would've forgotten it later on with all the stuff that needs to be done.
But yes, in the previous update you would've needed this line:
<include nearClass="com.alee.skin.web.WebSkin">resources/skin.xml</include>
to include the default light skin.
I'll close this as I guess the original question was answered.
I'm also working on the style loading fixes & performance improvements - those will be coming in v1.2.12 at the end of this month, but there are separate opened issues for that.
I'm creating WebLaF style editor and I don't know how to apply skin to component without creating
.xml
file. How can I do it?