Revision: 6341 Author: rj...@google.com Date: Fri Oct 9 17:08:00 2009 Log: Introduces support for DataResource to UiBinder, and you know what that means: custom mouse cursors! Shaped like hearts!
Review by b...@google.com http://gwt-code-reviews.appspot.com/77808 http://code.google.com/p/google-web-toolkit/source/detail?r=6341 Added: /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitDataResource.java /trunk/user/src/com/google/gwt/uibinder/sample/client/heart.cur Modified: /trunk/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitImageResource.java /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml /trunk/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitDataResource.java Fri Oct 9 17:08:00 2009 @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.uibinder.rebind.model; + +/** + * Models a method returning a DataResource on a generated ClientBundle. + */ +public class ImplicitDataResource { + private final String name; + private final String source; + + ImplicitDataResource(String name, String source) { + this.name = name; + this.source = source; + } + + public String getName() { + return name; + } + + public String getSource() { + return source; + } +} ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/uibinder/sample/client/heart.cur Fri Oct 9 17:08:00 2009 Binary file, no diff available. ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java Fri Oct 9 14:47:58 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/BundleWriter.java Fri Oct 9 17:08:00 2009 @@ -19,12 +19,14 @@ import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.DataResource; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.CssResource.Strict; import com.google.gwt.resources.client.ImageResource.ImageOptions; import com.google.gwt.resources.client.ImageResource.RepeatStyle; import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle; import com.google.gwt.uibinder.rebind.model.ImplicitCssResource; +import com.google.gwt.uibinder.rebind.model.ImplicitDataResource; import com.google.gwt.uibinder.rebind.model.ImplicitImageResource; /** @@ -38,10 +40,11 @@ private final TypeOracle oracle; private final JClassType clientBundleType; - private final JClassType strictAnnotationType; + private final JClassType dataResourceType; private final JClassType imageOptionType; private final JClassType imageResourceType; private final JClassType repeatStyleType; + private final JClassType strictAnnotationType; public BundleWriter(ImplicitClientBundle bundleClass, PrintWriterManager writerManager, TypeOracle oracle, @@ -53,10 +56,11 @@ this.oracle = oracle; clientBundleType = oracle.findType(ClientBundle.class.getName()); - strictAnnotationType = oracle.findType(Strict.class.getCanonicalName()); + dataResourceType = oracle.findType(DataResource.class.getCanonicalName()); imageOptionType = oracle.findType(ImageOptions.class.getCanonicalName()); imageResourceType = oracle.findType(ImageResource.class.getCanonicalName()); repeatStyleType = oracle.findType(RepeatStyle.class.getCanonicalName()); + strictAnnotationType = oracle.findType(Strict.class.getCanonicalName()); } public void write() throws UnableToCompleteException { @@ -76,25 +80,34 @@ } // Imports - writer.write("import %s;", imageResourceType.getQualifiedSourceName()); - writer.write("import %s;", imageOptionType.getQualifiedSourceName()); writer.write("import %s;", clientBundleType.getQualifiedSourceName()); + // TODO(rjrjr,bobv) Remove the strict import when strict becomes default, RSN writer.write("import %s;", strictAnnotationType.getQualifiedSourceName()); + writer.write("import %s;", dataResourceType.getQualifiedSourceName()); + writer.write("import %s;", imageResourceType.getQualifiedSourceName()); + writer.write("import %s;", imageOptionType.getQualifiedSourceName()); writer.newline(); // Open interface writer.write("public interface %s extends ClientBundle {", bundleClass.getClassName()); writer.indent(); - + // Write css methods for (ImplicitCssResource css : bundleClass.getCssMethods()) { + // TODO(rjrjr,bobv) Remove the @Strict when strict becomes default, RSN writer.write("@Strict @Source(\"%s\")", css.getSource()); writer.write("%s %s();", css.getClassName(), css.getName()); writer.newline(); } - - writer.newline(); + + // Write data methods + for (ImplicitDataResource data : bundleClass.getDataMethods()) { + writer.write("@Source(\"%s\")", data.getSource()); + writer.write("%s %s();", dataResourceType.getName(), data.getName()); + writer.newline(); + } + writeImageMethods(); // Close interface. ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java Fri Oct 9 14:47:58 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java Fri Oct 9 17:08:00 2009 @@ -20,11 +20,13 @@ import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.resources.client.CssResource; +import com.google.gwt.resources.client.DataResource; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.ImageResource.RepeatStyle; import com.google.gwt.uibinder.rebind.messages.MessagesWriter; import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle; import com.google.gwt.uibinder.rebind.model.ImplicitCssResource; +import com.google.gwt.uibinder.rebind.model.ImplicitDataResource; import com.google.gwt.uibinder.rebind.model.ImplicitImageResource; import com.google.gwt.uibinder.rebind.model.OwnerField; @@ -50,6 +52,7 @@ private final ImplicitClientBundle bundleClass; private final JClassType cssResourceType; private final JClassType imageResourceType; + private final JClassType dataResourceType; public UiBinderParser(UiBinderWriter writer, MessagesWriter messagesWriter, FieldManager fieldManager, TypeOracle oracle, @@ -61,6 +64,7 @@ this.bundleClass = bundleClass; this.cssResourceType = oracle.findType(CssResource.class.getCanonicalName()); this.imageResourceType = oracle.findType(ImageResource.class.getCanonicalName()); + this.dataResourceType = oracle.findType(DataResource.class.getCanonicalName()); } /** @@ -70,9 +74,7 @@ public String parse(XMLElement elem) throws UnableToCompleteException { // TODO(rjrjr) Clearly need to break these find* methods out into their own // parsers, an so need a registration scheme for uibinder-specific parsers - findStyles(elem); findResources(elem); - findImages(elem); messagesWriter.findMessagesConfig(elem); XMLElement uiRoot = elem.consumeSingleChildElement(); return writer.parseElementToField(uiRoot); @@ -110,15 +112,26 @@ return resourceType; } + /** + * Interprets <ui:data> elements + */ + private void createData(XMLElement elem) throws UnableToCompleteException { + String name = elem.consumeRequiredAttribute(FIELD_ATTRIBUTE); + String source = elem.consumeRequiredAttribute(SOURCE_ATTRIBUTE); + ImplicitDataResource dataMethod = bundleClass.createDataResource(name, source); + FieldWriter field = fieldManager.registerField(dataResourceType, + dataMethod.getName()); + field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(), + dataMethod.getName())); + } + /** * Interprets <ui:image> elements */ private void createImage(XMLElement elem) throws UnableToCompleteException { String name = elem.consumeRequiredAttribute(FIELD_ATTRIBUTE); - String source = elem.consumeAttribute(SOURCE_ATTRIBUTE, null); // @source is - // optional - // on - // ImageResource + // @source is optional on ImageResource + String source = elem.consumeAttribute(SOURCE_ATTRIBUTE, null); Boolean flipRtl = null; if (elem.hasAttribute(FLIP_RTL_ATTRIBUTE)) { @@ -208,51 +221,33 @@ field.setInitializer(String.format("%s.%s()", bundleClass.getFieldName(), cssMethod.getName())); } - - private void findImages(XMLElement binderElement) - throws UnableToCompleteException { - binderElement.consumeChildElements(new XMLElement.Interpreter<Boolean>() { - public Boolean interpretElement(XMLElement elem) - throws UnableToCompleteException { - if (!(writer.isBinderElement(elem) && "image".equals(elem.getLocalName()))) { - return false; // Not of interest, do not consume - } - - createImage(elem); - - return true; // Yum - } - }); - } private void findResources(XMLElement binderElement) throws UnableToCompleteException { binderElement.consumeChildElements(new XMLElement.Interpreter<Boolean>() { public Boolean interpretElement(XMLElement elem) throws UnableToCompleteException { - if (!(writer.isBinderElement(elem) && "with".equals(elem.getLocalName()))) { - return false; // Not of interest, do not consume - } - - createResource(elem); - - return true; // Yum - } - }); - } - - private void findStyles(XMLElement binderElement) - throws UnableToCompleteException { - binderElement.consumeChildElements(new XMLElement.Interpreter<Boolean>() { - public Boolean interpretElement(XMLElement elem) - throws UnableToCompleteException { - if (!(writer.isBinderElement(elem) && "style".equals(elem.getLocalName()))) { - return false; // Not of interest, do not consume - } - - createStyle(elem); - - return true; // consume + + if (writer.isBinderElement(elem)) { + final String localName = elem.getLocalName(); + if ("with".equals(localName)) { + createResource(elem); + } + else if ("image".equals(localName)) { + createImage(elem); + } + else if ("style".equals(localName)) { + createStyle(elem); + } + else if ("data".equals(localName)) { + createData(elem); + } + else { + writer.die("%s unrecognized, or not appropriate as a top level element"); + } + return true; + } + return false; // leave it be } }); } ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java Fri Oct 9 14:47:58 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java Fri Oct 9 17:08:00 2009 @@ -20,16 +20,17 @@ import com.google.gwt.uibinder.rebind.MortalLogger; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; /** * Models the ClientBundle to be generated from a ui.xml. */ public class ImplicitClientBundle { - - private final Set<ImplicitCssResource> cssMethods = new HashSet<ImplicitCssResource>(); - private final Set<ImplicitImageResource> imageMethods = new HashSet<ImplicitImageResource>(); + // LinkedHashSets for consistent order across recompiles + private final LinkedHashSet<ImplicitCssResource> cssMethods = new LinkedHashSet<ImplicitCssResource>(); + private final LinkedHashSet<ImplicitImageResource> imageMethods = new LinkedHashSet<ImplicitImageResource>(); + private final LinkedHashSet<ImplicitDataResource> dataMethods = new LinkedHashSet<ImplicitDataResource>(); private final String packageName; private final String className; private final String fieldName; @@ -54,7 +55,7 @@ /** * Called to declare a new CssResource accessor on this bundle. * - * @param name the method name + * @param name the method name and the ui:field name * @param source path to the .css file resource * @param extendedInterface the public interface implemented by this * CssResource, or null @@ -68,12 +69,26 @@ cssMethods.add(css); return css; } + + /** + * Called to declare a new DataResource accessor on this bundle. + * All params must be non-null + * + * @param name the method name and the ui:field name + * @param source path to the resource + * @return + */ + public ImplicitDataResource createDataResource(String name, String source) { + ImplicitDataResource data = new ImplicitDataResource(name, source); + dataMethods.add(data); + return data; + } /** * Called to declare a new ImageResource accessor on this bundle. * - * @param name the method name - * @param source path the image resource, or null if none was specified + * @param name the method name and the ui:field name + * @param source path to the image resource, or null if none was specified * @param flipRtl value for the flipRtl ImageOption, or null if none was * specified * @param repeatStyle value of the RepeatStyle ImageOption, or null if none @@ -94,11 +109,15 @@ public Set<ImplicitCssResource> getCssMethods() { return Collections.unmodifiableSet(cssMethods); } + + public Set<ImplicitDataResource> getDataMethods() { + return Collections.unmodifiableSet(dataMethods); + } public String getFieldName() { return fieldName; } - + public Set<ImplicitImageResource> getImageMethods() { return Collections.unmodifiableSet(imageMethods); } ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java Fri Oct 9 14:47:58 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java Fri Oct 9 17:08:00 2009 @@ -44,7 +44,7 @@ private final MortalLogger logger; private File generatedFile; - public ImplicitCssResource(String packageName, String className, String name, + ImplicitCssResource(String packageName, String className, String name, String source, JClassType extendedInterface, String body, MortalLogger logger) { this.packageName = packageName; ======================================= --- /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitImageResource.java Fri Oct 9 14:47:58 2009 +++ /trunk/user/src/com/google/gwt/uibinder/rebind/model/ImplicitImageResource.java Fri Oct 9 17:08:00 2009 @@ -26,7 +26,7 @@ private final Boolean flipRtl; private final RepeatStyle repeatStyle; - public ImplicitImageResource( + ImplicitImageResource( String name, String source, Boolean flipRtl, RepeatStyle repeatStyle) { this.name = name; this.source = source; ======================================= --- /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java Fri Oct 9 14:47:58 2009 +++ /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.java Fri Oct 9 17:08:00 2009 @@ -25,6 +25,7 @@ import com.google.gwt.dom.client.StyleInjector; import com.google.gwt.dom.client.TableElement; import com.google.gwt.resources.client.CssResource; +import com.google.gwt.resources.client.DataResource; import com.google.gwt.resources.client.ImageResource; import com.google.gwt.resources.client.CssResource.Shared; import com.google.gwt.uibinder.client.UiBinder; @@ -119,6 +120,7 @@ @UiField ImageResource prettyTilingImage; @UiField Image babyWidget; @UiField ParagraphElement simpleSpriteParagraph; + @UiField DataResource heartCursorResource; public WidgetBasedUi() { this.bundledLabel = new Label(); ======================================= --- /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml Fri Oct 9 14:47:58 2009 +++ /trunk/user/src/com/google/gwt/uibinder/sample/client/WidgetBasedUi.ui.xml Fri Oct 9 17:08:00 2009 @@ -96,6 +96,14 @@ } </ui:style> +<ui:style field='cursorifficStyle'> + @url cursor heartCursorResource; + .cursor { + cursor:cursor,pointer; + } +</ui:style> +<ui:data field='heartCursorResource' src='heart.cur'/> + <ui:style field='mySpritelyStyle'> @sprite .simpleSprite { gwt-image: "prettyImage"; @@ -174,14 +182,18 @@ </p> <p>I bet you like babies in your Image widgets.</p> + <div class='{cursorifficStyle.cursor}'> <gwt:Image ui:field='babyWidget' resource='{prettyImage}'/> + </div> <p ui:field='simpleSpriteParagraph' - class='{mySpritelyStyle.simpleSprite} {mySpritelyStyle.garish}' > + class='{mySpritelyStyle.simpleSprite} {mySpritelyStyle.garish} + {cursorifficStyle.cursor}' > And sprites too </p> - <p class='{mySpritelyStyle.tilingSprite} {mySpritelyStyle.garish}'> + <p class='{mySpritelyStyle.tilingSprite} {mySpritelyStyle.garish} + {cursorifficStyle.cursor}'> Well how do you like <br/> tiled sprited images...of babies!! <br/> Well of course you do. Who wouldn't? @@ -442,10 +454,12 @@ <b>HTML:</b> <pre style="border: 1px dashed #666; padding: 5px 0;"> <div id="gwt-debug-joe" - class="gwt-Label newStyle anotherStyle gwt-Label-dependentStyle gwt-Label-anotherDependentStyle"> + class="gwt-Label newStyle anotherStyle gwt-Label-dependentStyle + gwt-Label-anotherDependentStyle"> A label with a debug id </div> - <button id="gwt-debug-myButton" class="gwt-Button buttonStyle" tabindex="0" type="button">Go</button></pre> + <button id="gwt-debug-myButton" class="gwt-Button buttonStyle" tabindex="0" + type="button">Go</button></pre> <gwt:FlowPanel> <gwt:Label ui:field="lblDebugId" debugId="joe" addStyleNames="newStyle, anotherStyle" addStyleDependentNames="dependentStyle, anotherDependentStyle"> ======================================= --- /trunk/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java Fri Oct 9 14:47:58 2009 +++ /trunk/user/test/com/google/gwt/uibinder/sample/client/UiBinderTest.java Fri Oct 9 17:08:00 2009 @@ -25,6 +25,7 @@ import com.google.gwt.junit.Platform; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.DataResource; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.ui.DisclosurePanel; import com.google.gwt.user.client.ui.DockPanel; @@ -352,6 +353,16 @@ assertEquals(widgetUi.prettyImage.getLeft(), widgetUi.babyWidget.getOriginLeft()); } + interface HeartBundle extends ClientBundle { + @Source("heart.cur") + DataResource heart(); + } + + public void testDataResource() { + HeartBundle b = GWT.create(HeartBundle.class); + assertEquals(b.heart().getUrl(), widgetUi.heartCursorResource.getUrl()); + } + public void testSpritedElement() { assertEquals(widgetUi.prettyImage.getWidth(), widgetUi.simpleSpriteParagraph.getOffsetWidth()); assertEquals(widgetUi.prettyImage.getHeight(), widgetUi.simpleSpriteParagraph.getOffsetHeight()); --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---