CSS Resources
@Import
and @ImportedWithPrefix
A CSS Resource can import other CSS resources. GWT supplies two annotations
for this: @Import
and @ImportedWithPrefix
.Unfortunately, the online examples and tutorials that I have found for this are woefully incomplete. They all suffer from what I have found to be an ever-increasing approach to programming discussions and documentation: they have either been written hypothetically (i.e., the code they list was never actually run), or, if they did come from a working example, the small details crucial to understanding and implementing the concept have been left out.
Plus, to make things even more confusing, the
@Import
annotation
does not produce the same effect as using @import
within a CSS
file. When used in a CSS file associated with a CssResource
, @import
doesn't
seem to do anything.So, below is my attempt at describing these two annotations and how they are used.
A
CssResource
sub-interface can have a prefix automatically prepended
to its style class names by annotating it with the @ImportedWithPrefix("prefix")
annotation.
As a result, all the style class names
will have the prefix prepended, regardless of whether the CSS resource is imported
into another, of just used without importing it anywhere(a better name for the annotation might have been @PrefixedWith). In the associated
CSS file, however, you should write the class names without the prefix (which
would match the method names in the interface).Another CSS resource can import one or more additional
CssResource
classes
by creating a method that returns a CssResource
, annotated with @Import
.
The value supplied for the annotation can be a single class object for another CssResource
,
or an array of those class objects. Given that the imported class names will
have the prefix regardless of whether they're imported anywhere, the real
benefit of this is that the associated stylesheet can modify the rules involving
the imported class name (but, they would now use the prefix).For any class names that the importing CSS does want to use, there must be an associated method in the interface (but, without any prefix). It appears that the GWT compiler, as of GWT 2.5, will allow the CSS file to have any prefixed class names as long as the end part of the name is declared as a method in the interface, even if the specific prefix applied doesn't match anything that was imported.
@ImportedWithPrefix("prefix") public interface CssResourceNameToBePrefixed extends CssResource { public String accessorMethods(); }Within the associated CSS file, all the style class names used in the interface must be defined, but without the prefix. When the resource is imported into another, the resulting class definitions will have the prefix (of course, you won't be able to tell, since they will be obfuscated).
Note that by doing this, you are locking this resource into always using the specified prefix.
public interface BundleClass extends ClientBundle { @Source(value="CssResourceToBePrefixedCssFilePath") public CssResourceNameToBePrefixed accessorMethods(); @Import(value=Class<? extends CssResource>) @Source(value="ImportingCssResourceCssFilePath") public ImportingCssResourceClassName accessorMethods(); }The CSS file associated with the new CSS resource can also contain rules involving the imported classes, but now those rules must include the prefix.
But, to use the prefixed rules, your client bundle must have an instance of the CSS resource that originally defined them, not just the one that imported them. So, the client bundle containing the code snippet above, you must also define a method to retrieve the original CSS resource (the one with
@ImportedWithPrefix
). In fact, a CSS resource that uses the @ImportedWithPrefix
annotation
doesn't even have to be imported into any other - you can just invoke the accessor
methods to retrieve the obfuscated name, which will be different from the same
name defined in a different CSS resource with a different prefix or no prefix.But, if this resource is imported into another, any rules added by the importing resource's CSS file will be applied also, since that caused the CSS to be generated and injected.
A Working Example
The Eclipse ProjectNote that in order to minimize the file size, the
gwt-servlet.jar
file is not included. After unzipping and importing into an Eclipse workspace,
you can usually reinstate it by going to the project's properties, under the
Google ... Web Toolkit sections, removing the check from Use Google Web Toolkit,
then OK. Then go back through the same process to put the check back and OK
again. You can try the code below to see all three CSS resources in action. There are embedded notes regarding code to comment out to try it without any instance of the
Styles
resource - they must be done as a group./* Styles.css */ @external .gwt-HTML; .special { font-size: 14pt; } /* Try commenting out the two x-special rules below */ .a-special { font-size: 18pt; } .b-special { font-size: 22pt; } /* Not related to the example, just to make things look nicer */ html .gwt-HTML { margin-top: 12px; border: 1px solid blue; padding: 12px; border-radius: 6px; }
// Styles.java package whatever.client; import com.google.gwt.resources.client.CssResource; public interface Styles extends CssResource { public String special(); }
/* AStyles.css */ .special { background-color: #ffaaaa; }
// AStyles.java package whatever.client; import com.google.gwt.resources.client.CssResource; import com.google.gwt.resources.client.CssResource.ImportedWithPrefix; @ImportedWithPrefix("a") public interface AStyles //extends Styles // would also work with this instead of below extends CssResource { public String special(); }
/* BStyles.css */ .special { background-color: #aaaaff; }
// BStyles.java package whatever.client; import com.google.gwt.resources.client.CssResource; import com.google.gwt.resources.client.CssResource.ImportedWithPrefix; @ImportedWithPrefix("b") public interface BStyles //extends Styles // would also work with this instead of below extends CssResource { public String special(); }
// SomeResources.java package whatever.client; import com.google.gwt.core.client.GWT; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource.Import; public interface SomeResources extends ClientBundle { /* Try commenting out the three lines below, after * commenting out the prefixed lines in the Styles.css file */ @Import({AStyles.class, BStyles.class}) @Source("Styles.css") public Styles styles(); @Source("AStyles.css") public AStyles aStyles(); @Source("BStyles.css") public BStyles bStyles(); public static final SomeResources INSTANCE = GWT.create(SomeResources.class); }In the code below, the lines marked with
/**/
are to be commented
out to try it without any instance of the Styles
class.// SomeResources.java package whatever.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.*; public class CssResourceImports implements EntryPoint { private static final SomeResources resources = SomeResources.INSTANCE; private static final AStyles aStyles = resources.aStyles(); private static final BStyles bStyles = resources.bStyles(); private static final Styles styles = resources.styles(); /**/ public void onModuleLoad() { aStyles.ensureInjected(); bStyles.ensureInjected(); styles.ensureInjected(); /**/ FlowPanel panel = new FlowPanel(); HTML special = new HTML("Special"); /**/ special.addStyleName(styles.special()); /**/ panel.add( special ); /**/ HTML aSpecial = new HTML("A Special"); aSpecial.addStyleName(aStyles.special()); panel.add( aSpecial ); HTML bSpecial = new HTML("B Special"); bSpecial.addStyleName(bStyles.special()); panel.add( bSpecial ); RootPanel.get().add( panel ); } }