Author: [email protected]
Date: Wed May 20 14:19:56 2009
New Revision: 5446
Added:
wiki/CssResource.wiki
wiki/CssResourceCookbook.wiki
Log:
Copy CssResource documentation from incubator project into GWT trunk.
Added: wiki/CssResource.wiki
==============================================================================
--- (empty file)
+++ wiki/CssResource.wiki Wed May 20 14:19:56 2009
@@ -0,0 +1,557 @@
+#summary Compile-time CSS processing
+#labels Phase-Implementation,Type-Library
+
+<wiki:toc max_depth="2" />
+
+See also the CssResourceCookbook
+
+=Goals=
+
+ * Primary
+ * Compatibility with non-GWT-aware CSS parsers (i.e. any extensions
should be valid CSS syntax)
+ * This does not imply that the stylesheet would necessarily make
sense if you just displayed it in a browser
+ * Syntax validation
+ * Minification
+ * Leverage GWT compiler
+ * Different CSS for different browsers, automatically
+ * Static evaluation of content
+ * Secondary
+ * Basic CSS Modularization
+ * Via dependency-injection API style
+ * Widgets can inject their own CSS only when it's needed
+ * BiDi (Janus-style?)
+ * CSS image strips
+ * "Improve CSS"
+ * Constants
+ * Simple expressions
+ * Tertiary
+ * Runtime manipulation (StyleElement.setEnabled() handles many cases)
+ * Compile-time class-name checking (Java/CSS)
+ * Obfuscation
+
+
+=Non-Goals=
+ * Server-side manipulation
+ * All features in CssResource must be implemented with compile-time
and runtime code only. No features may depend on runtime support from
server-side code.
+
+=Overview=
+
+ 1 Write a CSS file, with or without GWT-specific extensions
+ 1 If GWT-specific extensions are used, define a custom subtype of
CssResource
+ 1 Declare a method that returns CssResource or a subtype in an
ImmutableResourceBundle
+ 1 When the bundle type is generated with `GWT.create()` a Java
expression that evaluates to the contents of the stylesheets will be created
+ * Except in the simplest case where the Java expression is a string
literal, it is generally not the case that a CSS file could be generated
into the module output
+ 1 At runtime, call `CssResource.getText()` to retrieve the contents of
the stylesheet
+ * This will typically be combined with StyleInjector
+
+=Features=
+
+== Constants ==
+(*Working*)
+{{{
+...@def small 1px;
+...@def black #000;
+border: small solid black;
+}}}
+ * The parse rules make it difficult to use delimiting tokens for
substitutions
+ * Redefining built-in sizes allows users to write plain CSS to draft a
style and then tweak it.
+ * Suggest that users use upper-case names, similar to static final
members.
+ * Any legal property value or expression may be used with `...@def`
+ * `...@def` rules that define a single numeric value may be accessed in a
manner similar to obfuscated class names by defining an accessor method on
the CssResource type that returns a primitive numeric value.
+ {{{
+interface MyResources extends CssResource {
+ int small();
+}
+}}}
+ * Calling `small()` would return the value `1`.
+
+== Runtime substitution ==
+(*Working*)
+{{{
+...@eval userBackground com.module.UserPreferences.getUserBackground();
+div {
+ background: userBackground;
+}
+}}}
+ * Provides runtime support for evaluating static methods when the
stylesheet is injected. Triggered / dynamic updates could be added in the
future if we allow programmatic manipulation of the style elements.
+ * If the user-defined function can be statically evaluated by the
compiler, then the implementation of the specific CssResource should
collapse to just a string literal.
+ * This allows easy support for non-structural skinning changes.
+
+== Value function ==
+{{{
+.myDiv {
+ offset-left: value('imageResource.getWidth', 'px');
+}
+}}}
+
+ * The `value()` function takes a sequence of dot-separated identifiers
and an optional suffix. The identifiers are interpreted as zero-arg method
invocations, using the interface passed to GWT.create() as the root
namespace. By only allowing zero-arg methods, there's no need to attempt
to perform type checking in the Generator. The only validation necessary
is to ensure that the sequence of methods exists. There may be arbitrarily
many identifiers in the chain.
+ * The `value()` function may be combined with `...@def`
+ {{{
+...@def SPRITE_WIDTH value('imageResource.getWidth', 'px')
+
+.selector {
+ width: SPRITE_WIDTH;
+}
+}}}
+
+== Conditional CSS ==
+(*Working*)
+{{{
+/* Runtime evaluation in a static context */
+...@if (com.module.Foo.staticBooleanFunction()) {
+ ... css rules ...
+}
+
+/* Compile-time evaluation */
+...@if <deferred-binding-property> <space-separated list of values> {
+ ... css rules ...
+}
+...@if user.agent safari gecko1_8 { ... }
+...@if locale en { ... }
+
+/* Negation is supported */
+...@if !user.agent ie6 opera {
+ ...
+}
+
+/* Chaining is also supported */
+...@if (true) {
+} @elif (false) {
+} @else {
+}
+}}}
+ * This allows for more advanced skinning / theming / browser quirk
handling by allowing for structural changes in the CSS.
+ * The contents of an @if block can be anything that would be a top-level
rule in a CSS stylesheet.
+ * @if blocks can be arbitrarily nested.
+ * What does it mean to have an @def or @eval in an @if block? Easy to
make this work for property-based @if statements; would have to generate
pretty gnarly runtime code to handle the expression-based @if statement.
Could have block-level scoping; but this seems like a dubious use-case.
+ * If the function in the first form can be statically evaluated by the
compiler in a permutation, there is no runtime cost. The second form will
never have a runtime cost because it is evaluated during compilation.
+
+== Image Sprites ==
+(*Working*):
+{{{
+...@sprite .mySpriteClass {gwt-image: "imageAccessor"; other: property;} =>
generates =>
+ .mySpriteClass {
+ background-image: url(gen.png);
+ clip: ...;
+ width: 27px;
+ height: 42px;
+ other: property;
+ }
+}}}
+{{{
+class MyCssResource extends CssResource {
+ String mySpriteClass();
+}
+
+class MyResources extends ImmutableResourceBundle {
+ @Resource("my.css")
+ MyCssResource css();
+
+ @Resource("some.png")
+ ImageResource imageAccessor();
+
+ @Resource("some.png")
+ @ImageOptions(repeatStyle=RepeatStyle.Horizontal)
+ ImageResource repeatingImage();
+}
+}}}
+ * @sprite is sensitive to the FooBundle in which the CSSResource is
declared; a sibling ImageResource method named in the @sprite declaration
will be used to compose the background sprite.
+ * @sprite entries will be expanded to static CSS rules, possibly with
data: urls.
+ * The expansion is sensitive to any RepeatStyle value defined on the
ImageResource accessor function. The appropriate `repeat-x` or `repeat-y`
properties will be added to the @sprite selector.
+ * Any CSS selector can be specified for @sprite.
+ * Support for IE6 isn't feasible in this format, because structural
changes to the DOM are necessary to implement a "windowing" effect. Once
it's possible to distinguish ie6 and ie7 in user.agent, we could revisit
support for ie6. In the current implementation, the ie6 code won't render
correctly, although is a purely cosmetic issue.
+
+== References to Data Resources ==
+(*Working*)
+{{{
+...@url myCursorUrl fancyCursorResource;
+
+.myClass {
+ cursor: myCursorUrl, pointer;
+}
+}}}
+ * The identifier will be expanded to `url('some_url')`
+
+== RTL support ==
+(*Working*)
+ * CssResource supports automatic transformations of CSS code into a
right-to-left variant at compile time.
+ * The use of the RTL variant is keyed by
`com.google.gwt.i18n.client.LocaleInfo.getCurrentLocale().isRTL()`
+ * Transformations applied:
+ * The `left` and `right` properties are flipped.
+ * Any properties that have values `left` or `right` are flipped:
`clear float text-align page-break-before page-break-after`
+ * The `background`/`background-position` property is flipped.
Attachments expressed in percentage points are mirrored: 40% becomes 60%
+ * `margin padding border-color border-style` and `border-width`
four-valued properties are flipped: `1px 2px 3px 4px` becomes `1px 4px 3px
2px`
+ * Any `xyz-right` or `xzy-right-abc` property is flipped to `xzy-left`
or `xzy-left-abc`
+ * The `direction` property on a `body` selector will be flipped from
`ltr` to `rtl`; on any other selectors, the `direction` property is
unchanged
+ * When the cursor property has an `resize` value, it will be flipped:
`ne-resize` becomes `nw-resize`
+ * Sections of CSS can be exempted from automatic flipping by enclosing
it in a `...@noflip` block:
+ {{{
+...@noflip {
+ .selector {
+ left: 10;
+ }
+}
+}}}
+ * A `background` property value that uses pixel-based offsets, such as
`background-position: 4px 10px;` will not be transformed automatically.
+ * The four-valued CSS3 `background-position` property will be
automatically flipped by the RTL support
+ {{{
+background-position: left 4px top 10px;
+}}}
+ * For CSS2 browsers, it will be necessary to use an `...@sprite` rule:
+ {{{
+...@sprite .bgImage {
+ gwt-image: 'background-image';
+ position: absolute;
+ left: 4px;
+ top: 10px;
+}
+}}}
+ * `ImageResources` can be automatically flipped in RTL contexts via the
use of the `...@imageoptions` annotation:
+ {{{
+...@resource("icon128.png")
+...@imageoptions(flipRtl = true)
+ImageResource logo();
+}}}
+ *
[http://google-web-toolkit.googlecode.com/svn/trunk/user/test/com/google/gwt/resources/rg
Current auto-RTL test cases]
+
+== Selector obfuscation ==
+(*Working*):
+{{{
+java:
+ class Resources {
+ MyCSSResource myCSSResource();
+ }
+ class MyCSSResource extends CSSResource {
+ Sprite mySpriteClass();
+ String someOtherClass();
+ String hookClass();
+ }
+ myWidget.addStyleName(resource.mySpriteClass());
+
+css:
+ @sprite mySpriteClass mySpriteImage;
+ .someOtherClass {
+ /* ... */
+ }
+ .hookClass{} /* Empty and stripped, but left for future expansion */
+}}}
+ * The function just returns the CSS class name, but verifies that the
CSS class exists in the stylesheet.
+ * _TODO_: Add an @external annotation to allow a "binding" to a
site-wide css file
+ * No typos.
+ * For obfuscation, we'll use a Adler32 checksum of the source css file
expressed in base36 as a prefix (7 chars). The developer can override this
with the `CssResource.globalPrefix` deferred-binding property.
+ * `<set-property name="CssResource.globalPrefix" value="empty" />` can
be used for minimal-length selector names, but this is only recommended
when the GWT module has total control over the page.
+ * Instead a combination of `<extend-property>` and `<set-property>`
can be used to prove a shorter, but probably unique, prefix.
+ * This will transition to `<set-configuration-property>` when moved
into GWT trunk.
+
+= Unplanned use cases =
+
+These use cases are not planned, but remain here for the sake of
completeness.
+
+== Compile-time import ==
+{{{
+...@import cssResourceFunctionName;
+}}}
+ * What does this mean?
+ * Is this a preprocessor-like #include? If so, it is problematic to
have an n:1 relationship?
+ * Is this like a Java static import where we just inherit metadata
like @def or @url?
+ * To what extent is this actually necessary? The point of the @import
statement is to allow for source and deployment modularization. Source
modularization can be achieved by using multiple CssResource interfaces and
we're only concerned with static resources in the Bundle.
+ * The @import statement will only work for other CssResources, not for
URLs at runtime, since the `.gwt.xml` or StyleInjector can be used in those
cases.
+
+*Conclusion:* too confusing. The modularity afforded by CSS's `...@import`
statement is inherent in the CssResource design.
+
+== Implementation fix-ups ==
+(~~Not planned~~):
+{{{
+ div:hover { }
+ Could use top-level event handler to fake on IE
+ div:focus { }
+ Problematic -- focus/blur don't bubble
+}}}
+
+*Conclusion:* technically possible, but likely unaffordable at runtime.
The rendering engines that don't provide the wanted features in the first
place likely don't provide the means to implement those missing features
efficiently.
+
+=Optimizations=
+
+==Basic minification==
+
+Basic minification of the CSS input results in the minimum number of bytes
required to retain the original structure of the input. In general, this
means that comments, unnecessary whitespace, and empty rules are removed.
+
+{{{
+.div {
+ /* This is the default background color */
+ background: blue;
+}
+.empty {}
+}}}
+
+would be transformed into
+
+{{{
+.div{background:blue;}
+}}}
+
+==Selector merging==
+
+Rules with identical selectors can be merged together.
+
+{{{
+.div {prop: value;}
+.div {foo: bar;}
+}}}
+
+becomes
+
+{{{
+.div {prop:value;foo:bar;}
+}}}
+
+However, it is necessary that the original semantic ordering of the
properties within the CSS is preserved. To ensure that all selector merges
are correct, we impose the restriction that *no rule can be promoted over
another if the two rules define a common property*. We consider `border`
and `border-top` to be equivalent properties, however `padding-left` and
`padding-right` are not equivalent.
+
+Thus
+
+{{{
+.a {background: green;}
+.b {border: thin solid blue;}
+.a {border-top: thin solid red;}
+}}}
+
+cannot be merged because an element whose CSS class matches both `.a` and
`.b` would be rendered differently based on the exactly order of the CSS
rules.
+
+When working with `...@if` statements, it is preferable to work with the form
that operates on deferred-binding properties because the CSS compiler can
evaluate these rules statically, before the merge optimizations. Consider
the following:
+
+{{{
+.a {
+ background: red;
+}
+
+...@if user.agent safari {
+ .a {
+ \-webkit-border-radius: 5px;
+ }
+} @else {
+ .a {
+ background: url('picture_of_border.png');
+ }
+}
+}}}
+
+In the safari permutation, the rule becomes
`.a{background:red;\-webkit-border-radius:5px;}` while in other
permutations, the `background` property is merged.
+
+==Property merging==
+
+Rules with identical properties can be merged together.
+
+{{{
+.a {background: blue;}
+.b {background: blue;}
+}}}
+
+can be transformed into
+
+{{{
+.a,.b{background:blue;}
+}}}
+
+Promotion of rules follows the previously-established rule of not
promoting a rule over other rules with common properties.
+
+=Levers and Knobs=
+
+ * The deferred-binding property `CssResource.style` may be set to
`pretty` which will disable class-name obfuscation as well as pretty-print
the CSS content. Combine this with a `ResourceBundle.enableInlining` value
of `false` to produce a CSS expression which is amenable to client-side
editing.
+ * The deferred-binding property `CssResoure.enableMerge` can be set to
`false` to disable modifications that re-order rules. This should be
considered a temporary measure until the merge logic has been fully vetted.
+ * To allow for client-side tweaking of the effective (i.e.
permutation-specific) style rules, you can store the value of
CssResource.getText() into a TextArea. Wire some UI action to pass the
contents of the TextArea into `StyleInjector.setContents()` to overwrite
the original, injected stylesheet.
+
+= Selector obfuscation details =
+== Scope ==
+Scoping of obfuscated class names is defined by the return type of the
CssResource accessor method in the resource bundle. Each distinct return
type will return a wholly separate collection of values for String accessor
methods.
+
+{{{
+interface A extends CssResource {
+ String foo();
+}
+
+interface B extends A {
+ String foo();
+}
+
+interface C extends A {
+ String foo();
+}
+
+interface D extends C {
+ // Intentionally not defining foo()
+}
+
+interface Resources {
+ A a();
+ A a2();
+ B b();
+ C c();
+ D d();
+ D d2();
+}}}
+
+It will be true that a().foo() != b().foo() != c().foo() != d().foo().
However, a().foo() == a2().foo() and d().foo() == d2().foo().
+
+== Shared scopes ==
+
+In the case of "stateful" CSS classes like `focused` or `enabled`, it is
convenient to allow for certain String accessor functions to return the
same value, regardless of the CssResource type returned from the accessor
method.
+
+{{{
+
+...@shared
+interface FocusCss extends CssResource {
+ String focused();
+ String unfocused();
+}
+
+interface A extends FocusCss {
+ String widget();
+}
+
+interface B extends FocusCss {
+ String widget();
+}
+
+interface C extends B {
+ // Intentionally empty
+}
+
+interface Resources {
+ A a();
+ B b();
+ C c();
+ FocusCss f();
+}
+}}}
+
+In this example, a().focused() == b().focused() == c().focused ==
f().focused(). However, a().widget() != b().widget != c.widget(), as in
the previous example.
+
+The short version is that if distinct CSS types need to share obfuscated
class names, the CssResource subtypes to which they are attached must share
a common supertype that defines accessors for those names and has the
`...@shared` annotation.
+
+== Imported scopes ==
+
+The Java type system can be somewhat ambiguous when it comes to multiple
inheritance of interfaces that define methods with identical signatures,
although there exist a number of cases where it is necessary to refer to
multiple, unrelated CssResource types. Consider the case of a Tree that
contains Checkboxes.
+
+{{{
+...@importedwithprefix("tree")
+interface TreeCss extends CssResource {
+ String widget();
+}
+
+...@importedwithprefix("checkbox")
+interface CbCss extends CssResource {
+ String widget();
+}
+
+interface MyCss extends CssResource {
+ String other();
+}
+
+interface Resources {
+ @Import({TreeCss.class, CbCss.class})
+ MyCss css();
+}
+}}}
+{{{
+/* Now we can write a descendant selector using the prefixes defined on
the CssResource types */
+.tree-widget .checkbox-widget {
+ color: red;
+}
+
+.other {
+ something: else;
+}
+}}}
+
+Composing a "TreeCbCss" interface would be insufficient because consumers
of the TreeCss interface and CbCss interface would receive the same value
from the widget method. Moreover, the use of just `.widget` in the
associated CSS file would also be insufficient without the use of some kind
of class selector prefix. The prefix is defined on the CssResource type
(instead of on the CssResource accessor method) In the interest of
uniformity across all CSS files that import a given scope. It is a
compile-time error to import multiple classes that have the same prefix or
simple name.
+
+The case of shared scopes could be handled solely with importing scopes,
however this form is somewhat more verbose and relationships between
unrelated scopes is less common than the use of stateful selectors.
+
+=== Example: StackPanel inside a StackPanel ===
+
+This is a use-case that is currently impossible to style correctly in GWT.
+
+{{{
+// Assume this interface is provided by the UI library
+interface StackPanelCss extends CssResource {
+ String widget();
+ // and many more class names
+}
+
+// App code defines the following interfaces:
+
+...@importedwithprefix("inner")
+interface StackPanelInner extends StackPanelCss {
+ // Empty interface
+}
+
+interface StackPanelOuter extends StackPanelCss {
+ // Empty interface
+}
+
+interface Resources {
+ @Resource("stackPanel.css")
+ @Strict
+ StackPanelInner inner();
+
+ @Import(StackPanelInner.class)
+ @Resource("stackPanel.css", "outer.css")
+ @Strict
+ StackPanelOuter outer();
+}
+}}}
+
+The file `stackPanel.css` defines the basic structure of any given
stackPanel:
+{{{
+.widget .title {}
+.widget .content {}
+/* Other stuff to make a StackPanel work */
+}}}
+
+The `outer()` method can continue to use the base `stackPanel.css` file,
because the accessor methods defined in `StackPanelCss` are mapped into the
default (no-prefix) namespace. The inner StackPanel's style members are
also available, but in the `inner` prefix. Here's what `outer.css` might
contain:
+{{{
+.widget {color: red;}
+
+.inner-widget {
+ color: blue;
+ font-size: smaller;
+}
+}}}
+
+== Strict scoping ==
+
+In the normal case, any class selectors that do not match String accessor
functions are left unobfuscated in the compiled output.
+
+{{{
+interface MyCssResource extends CssResource {
+ String foo();
+}
+
+interface Resources {
+ @Strict
+ @Resource("my.css")
+ MyCssResource css();
+}
+}}}
+{{{
+/* This is ok */
+.foo {}
+
+/* This would generate a compile error in @Strict mode */
+.other {}
+}}}
+
+The `...@strict` annotation can be applied to a CssResource accessor function
to make it a compile-time error to have any unobfuscated class selectors in
the CSS file. This additional level of restriction is recommended for
library-oriented resource bundles in order to avoid inadvertently polluting
the global CSS namespace.
+
+Strict scoping can be forced on for all CssResources by adding
`<set-property name="CssResource.forceStrict" value="true" />` to the
module XML file, however this is only recommended for use by applications
or test modules and never in any module that will be redistributed.
+
+
+=Important open questions=
+ * Can we manipulate style rules across browsers?
+ * Maybe make MyCSSResource.mySpriteClass() return Rule object
+ * The @eval rule implies that stylesheets must be removable or can be
updated in-place.
+ * Does doing so update actual rendering on all browsers?
+ * Other meta-classes
+ * @Opacity
+ * @RoundedCorners
+ * Can we generalize the meta-classes into an extensible Java
provider API?
Added: wiki/CssResourceCookbook.wiki
==============================================================================
--- (empty file)
+++ wiki/CssResourceCookbook.wiki Wed May 20 14:19:56 2009
@@ -0,0 +1,217 @@
+#summary Various examples of how to use CssResource
+#labels Type-Guide
+
+This document is currently under construction.
+
+= Browser-specific css =
+
+{{{
+.foo {
+ background: green;
+}
+
+...@if user.agent ie6 {
+ /* Rendering fix */
+ .foo {
+ position: relative;
+ }
+} @elif user.agent safari {
+ .foo {
+ \-webkit-border-radius: 4px;
+ }
+} @else {
+ .foo {
+ font-size: x-large;
+ }
+}
+}}}
+
+= Obfuscated CSS class names =
+
+CssResource will use method names as CSS class names to obfuscate at
runtime.
+
+{{{
+interface MyCss extends CssResource {
+ String className();
+}
+
+interface MyResources extends ImmutableResourceBundle {
+ @Resource("my.css")
+ MyCss css();
+}
+}}}
+
+All instances of a selector with `.className` will be replaced with an
obfuscated symbol when the CSS is compiled. To use the obfuscated name:
+
+{{{
+MyResources resources = GWT.create(MyResources.class);
+Label l = new Label("Some text");
+l.addStyleName(resources.css().className());
+}}}
+
+If you have class names in your css file that are not legal Java
identifiers, you can use the `...@classname` annotation on the accessor method:
+{{{
+interface MyCss extends CssResource {
+ @ClassName("some-other-name")
+ String someOtherName();
+}
+}}}
+
+= Background images / Sprites =
+
+CssResource reuses the ImageResource bundling techniques and applies them
to CSS background images. This is generally known as "spriting" and a
special `...@sprite` rule is used in CssResource.
+
+{{{
+interface MyResources extends ImmutableResourceBundle {
+ @Resource("image.png")
+ ImageResource image();
+
+ @Resource("my.css");
+ CssResource css();
+}
+}}}
+
+In `my.css`, sprites are defined using the `...@sprite` keyword, followed by
an arbitrary CSS selector, and the rule block must include a `gwt-image`
property. The `gwt-image` property should name the ImageResource accessor
function.
+{{{
+...@sprite .myImage {
+ gwt-image: 'image';
+}
+}}}
+
+The elements that match the given selection will display the named image
and have their heights and widths automatically set to that of the image.
+
+== Tiled images ==
+
+If the ImageResource is decorated with an `...@imageoptions` annotation, the
source image can be tiled along the X- or Y-axis. This allows you to use
1-pixel wide (or tall) images to define borders, while still taking
advantage of the image bundling optimizations afforded by ImageResource.
+
+{{{
+interface MyResources extends ImmutableResourceBundle {
+ @ImageOptions(repeatStyle = RepeatStyle.Horizontal)
+ @Resource("image.png")
+ ImageResource image();
+}
+}}}
+
+The elements that match the `...@sprite`'s selector will only have their
height or width set, based on the direction in which the image is to be
repeated.
+
+== 9-boxes ==
+
+In order to make the content area of a 9-box have the correct size, the
height and widths of the border images must be taken into account. Instead
of hard-coding the image widths into your CSS file, you can use the
`value()` CSS function to insert the height or width from the associated
ImageResource.
+
+TODO: Move this setup into DecoratorPanel or other Widget that just
accepts the following Resources interface.
+
+{{{
+ public interface Resources extends ImmutableResourceBundle {
+ Resources INSTANCE = GWT.create(Resources.class);
+
+ @Resource("bt.png")
+ @ImageOptions(repeatStyle = RepeatStyle.Horizontal)
+ ImageResource bottomBorder();
+
+ @Resource("btl.png")
+ ImageResource bottomLeftBorder();
+
+ @Resource("btr.png")
+ ImageResource bottomRightBorder();
+
+ @Resource("StyleInjectorDemo.css")
+ CssResource css();
+
+ @Resource("lr.png")
+ @ImageOptions(repeatStyle = RepeatStyle.Vertical)
+ ImageResource leftBorder();
+
+ @Resource("rl.png")
+ @ImageOptions(repeatStyle = RepeatStyle.Vertical)
+ ImageResource rightBorder();
+
+ @Resource("tb.png")
+ @ImageOptions(repeatStyle = RepeatStyle.Horizontal)
+ ImageResource topBorder();
+
+ @Resource("tbl.png")
+ ImageResource topLeftBorder();
+
+ @Resource("tbr.png")
+ ImageResource topRightBorder();
+ }
+}}}
+
+{{{
+.contentArea {
+ padding: value('topBorder.getHeight', 'px')
value('rightBorder.getWidth', 'px')
+ value('bottomBorder.getHeight', 'px')
value('leftBorder.getWidth', 'px');
+}
+
+...@sprite .contentAreaTopLeftBorder {
+ gwt-image: 'topLeftBorder';
+ position: absolute;
+ top:0;
+ left: 0;
+}
+
+...@sprite .contentAreaTopBorder {
+ gwt-image: 'topBorder';
+ position: absolute;
+ top: 0;
+ left: value('topLeftBorder.getWidth', 'px');
+ right: value('topRightBorder.getWidth', 'px');
+}
+
+...@sprite .contentAreaTopRightBorder {
+ gwt-image: 'topRightBorder';
+ position: absolute;
+ top:0;
+ right: 0;
+}
+
+...@sprite .contentAreaBottomLeftBorder {
+ gwt-image: 'bottomLeftBorder';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+}
+
+...@sprite .contentAreaBottomBorder {
+ gwt-image: 'bottomBorder';
+ position: absolute;
+ bottom: 0;
+ left: value('bottomLeftBorder.getWidth', 'px');
+ right: value('bottomRightBorder.getWidth', 'px');
+}
+
+...@sprite .contentAreaBottomRightBorder {
+ gwt-image: 'bottomRightBorder';
+ position: absolute;
+ bottom: 0;
+ right: 0;
+}
+
+...@sprite .contentAreaLeftBorder {
+ gwt-image: 'leftBorder';
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+}
+
+...@sprite .contentAreaRightBorder {
+ gwt-image: 'rightBorder';
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 100%;
+}
+}}}
+{{{
+<div class="contentArea">
+<div class="contentAreaTopLeftBorder"></div>
+<div class="contentAreaTopBorder"></div>
+<div class="contentAreaRightBorder"></div>
+<div class="contentAreaBottomLeftBorder"></div>
+<div class="contentAreaBottomBorder"></div>
+<div class="contentAreaBottomRightBorder"></div>
+<div class="contentAreaLeftBorder"></div>
+<div class="contentAreaRightBorder"></div>
+</div>
+}}}
\ No newline at end of file
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---