Adds clipping behavior to Image (which necessitated an update to the setElement() method in UIObject and Widget to support arbitrary changes to a widget's root element). Also lays the foundation for image bundles with AbstractImagePrototype. Note the addition clear.cache.gif, which is required for clipped images but will now be a standard well-known image. It's a 1x1 pixel transparent image that can be cached infinitely. It might be useful for other purposes -- even in application code -- in addition to clipped Image.java. Patch by: rdayal Review by: bruce git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@726 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/javadoc/com/google/gwt/examples/ImageExample.java b/user/javadoc/com/google/gwt/examples/ImageExample.java index 41d640c..7777d98 100644 --- a/user/javadoc/com/google/gwt/examples/ImageExample.java +++ b/user/javadoc/com/google/gwt/examples/ImageExample.java
@@ -22,35 +22,59 @@ import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.ClickListener; public class ImageExample implements EntryPoint { private Label lbl = new Label(); + private Button btn = new Button("Clip this image"); + private Button btn2 = new Button("Restore image"); public void onModuleLoad() { - // Create an image, not yet referencing a URL. - Image image = new Image(); + // Create an image, not yet referencing a URL. We make it final so that we + // can manipulate the image object within the ClickHandlers for the buttons + final Image image = new Image(); // Hook up a load listener, so that we can find out when it loads (or - // fails to, as the case may be). + // fails to, as the case may be). Once the image loads, we can enable the + // buttons that are used to manipulate the images image.addLoadListener(new LoadListener() { - public void onLoad(Widget sender) { - lbl.setText("Done loading."); - } - public void onError(Widget sender) { lbl.setText("An error occurred while loading."); } + + // This event may not fire if the image is already cached + public void onLoad(Widget sender) { + lbl.setText("Done loading."); + } }); // Point the image at a real URL. lbl.setText("Loading..."); image.setUrl("http://www.google.com/images/logo.gif"); - // Add the image & label to the root panel. + // When the user clicks this button, we want to clip the image + btn.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + image.setVisibleRect(70, 0, 47, 100); + } + }); + + // When the user clicks this button, we want to restore the image to its + // unclipped state + btn2.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + image.setUrl("http://www.google.com/images/logo.gif"); + } + }); + + // Add the image, label, and clip/restore buttons to the root panel. VerticalPanel panel = new VerticalPanel(); panel.add(lbl); panel.add(image); + panel.add(btn); + panel.add(btn2); RootPanel.get().add(panel); } -} \ No newline at end of file +}
diff --git a/user/src/com/google/gwt/user/ClippedImage.gwt.xml b/user/src/com/google/gwt/user/ClippedImage.gwt.xml new file mode 100644 index 0000000..c4ba233 --- /dev/null +++ b/user/src/com/google/gwt/user/ClippedImage.gwt.xml
@@ -0,0 +1,25 @@ +<!-- --> +<!-- Copyright 2007 Google Inc. All Rights Reserved. --> +<!-- --> +<!-- This module is required in order to use an Image in its clipped state, --> +<!-- or an ImageBundle. --> +<!-- --> +<!-- The implementation differs between Internet Explorer and other --> +<!-- browsers, so we use deferred binding to create the correct instance of --> +<!-- the class based on the user's browser. --> +<!-- --> +<!-- This module is interited by the User module, so it will be available --> +<!-- to all GWT applications. --> + +<module> + <inherits name="com.google.gwt.core.Core" /> + <inherits name="com.google.gwt.user.UserAgent" /> + + <!-- IE needs a different implementation --> + <replace-with + class="com.google.gwt.user.client.ui.impl.ClippedImageImplIE6"> + <when-type-is + class="com.google.gwt.user.client.ui.impl.ClippedImageImpl" /> + <when-property-is name="user.agent" value="ie6" /> + </replace-with> +</module>
diff --git a/user/src/com/google/gwt/user/User.gwt.xml b/user/src/com/google/gwt/user/User.gwt.xml index adbf794..5ce6852 100644 --- a/user/src/com/google/gwt/user/User.gwt.xml +++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -14,4 +14,5 @@ <inherits name="com.google.gwt.user.Form"/> <inherits name="com.google.gwt.user.TextBox"/> <inherits name="com.google.gwt.user.Focus"/> + <inherits name="com.google.gwt.user.ClippedImage"/> </module>
diff --git a/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java b/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java new file mode 100644 index 0000000..8656716 --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java
@@ -0,0 +1,70 @@ +/* + * Copyright 2007 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.user.client.ui; + +/** + * An opaque representation of a particular image such that the image can be + * accessed either as an HTML fragment or as an {@link Image} object. An image + * protype can be thought of as an abstract image factory with additional + * capabilities. + * + * <p> + * The {@link #applyTo(Image)} method provides an efficient way to replace the + * contents of an existing <code>Image</code>. This is useful in cases where + * an image changes its appearance based on a user's action. Instead of creating + * two <code>Image</code> objects then alternately hiding/showing them, one + * can use the {@link #applyTo(Image)} method of two + * <code>AbstractImagePrototype</code> objects to transform a single + * <code>Image</code> object between two (or more) visual representations. The + * use of <code>AbstractImagePrototypes</code> results in an cleaner and more + * efficient implementation. + * </p> + * + * <p> + * This class is also a useful way to encapsulate complex HTML that represents + * an image without actually instantiating <code>Image</code> objects. When + * constructing large HTML fragments, especially those that contain many images, + * {@link #getHTML()} can be much more efficient. + * </p> + */ +public abstract class AbstractImagePrototype { + + /** + * Transforms an existing {@link Image} into the image represented by this + * prototype. + * + * @param image the instance to be transformed to match this prototype + */ + public abstract void applyTo(Image image); + + /** + * Creates a new {@link Image} instance based on the image represented by this + * prototype. + * + * @return a new <code>Image</code> based on this prototype + */ + public abstract Image createImage(); + + /** + * Gets an HTML fragment that displays the image represented by this + * prototype. The HTML returned is not necessarily a simple + * <code><img></code> element. It may be a more complex structure that + * should be treated opaquely. + * + * @return the HTML representation of this prototype + */ + public abstract String getHTML(); +}
diff --git a/user/src/com/google/gwt/user/client/ui/Image.java b/user/src/com/google/gwt/user/client/ui/Image.java index 331d54d..37330b1 100644 --- a/user/src/com/google/gwt/user/client/ui/Image.java +++ b/user/src/com/google/gwt/user/client/ui/Image.java
@@ -1,5 +1,5 @@ /* - * Copyright 2006 Google Inc. + * Copyright 2007 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 @@ -16,19 +16,46 @@ package com.google.gwt.user.client.ui; import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.DeferredCommand; +import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.impl.ClippedImageImpl; +import com.google.gwt.core.client.GWT; import java.util.HashMap; /** - * A widget that displays the image at a given URL. + * A widget that displays the image at a given URL. The image can be in + * 'unclipped' mode (the default) or 'clipped' mode. In clipped mode, a viewport + * is overlaid on top of the image so that a subset of the image will be + * displayed. In unclipped mode, there is no viewport - the entire image will be + * visible. Whether an image is in clipped or unclipped mode depends on how the + * image is constructed, and how it is transformed after construction. Methods + * will operate differently depending on the mode that the image is in. These + * differences are detailed in the documentation for each method. + * + * <p> + * If an image transitions between clipped mode and unclipped mode, any + * {@link Element}-specific attributes added by the user (including style + * attributes, style names, and style modifiers), except for event listeners, + * will be lost. + * </p> * * <h3>CSS Style Rules</h3> * <ul class="css"> * <li>.gwt-Image { }</li> * </ul> * + * Tranformations between clipped and unclipped state will result in a loss of + * any style names that were set/added; the only style names that are preserved + * are those that are mentioned in the static CSS style rules. Due to + * browser-specific HTML constructions needed to achieve the clipping effect, + * certain CSS attributes, such as padding and background, may not work as + * expected when an image is in clipped mode. These limitations can usually be + * easily worked around by encapsulating the image in a container widget that + * can itself be styled. + * * <p> * <h3>Example</h3> * {@example com.google.gwt.examples.ImageExample} @@ -38,6 +65,206 @@ SourcesMouseEvents, SourcesLoadEvents { /** + * Abstract class which is used to hold the state associated with an image + * object. + */ + private abstract static class State { + + public abstract int getHeight(Image image); + + public abstract int getOriginLeft(); + + public abstract int getOriginTop(); + + public abstract String getUrl(Image image); + + public abstract int getWidth(Image image); + + public abstract void setUrl(Image image, String url); + + public abstract void setUrlAndVisibleRect(Image image, String url, + int left, int top, int width, int height); + + public abstract void setVisibleRect(Image image, int left, int top, + int width, int height); + + // This method is used only by unit tests. + protected abstract String getStateName(); + } + + /** + * Implementation of behaviors associated with the unclipped state of an + * image. + */ + private static class UnclippedState extends State { + + UnclippedState(Image image) { + image.setElement(DOM.createImg()); + image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD + | Event.ONERROR); + } + + UnclippedState(Image image, String url) { + this(image); + setUrl(image, url); + } + + public int getHeight(Image image) { + return DOM.getIntAttribute(image.getElement(), "height"); + } + + public int getOriginLeft() { + return 0; + } + + public int getOriginTop() { + return 0; + } + + public String getUrl(Image image) { + return DOM.getAttribute(image.getElement(), "src"); + } + + public int getWidth(Image image) { + return DOM.getIntAttribute(image.getElement(), "width"); + } + + public void setUrl(Image image, String url) { + DOM.setAttribute(image.getElement(), "src", url); + } + + public void setUrlAndVisibleRect(Image image, String url, int left, + int top, int width, int height) { + image.changeState(new ClippedState(image, url, left, top, width, height)); + } + + public void setVisibleRect(Image image, int left, int top, int width, + int height) { + image.changeState(new ClippedState(image, getUrl(image), left, top, + width, height)); + } + + // This method is used only by unit tests. + protected String getStateName() { + return "unclipped"; + } + } + + /** + * Implementation of behaviors associated with the clipped state of an image. + */ + private static class ClippedState extends State { + + private static final ClippedImageImpl impl = (ClippedImageImpl) GWT.create(ClippedImageImpl.class); + + private int left = 0; + private int top = 0; + private int width = 0; + private int height = 0; + private String url = null; + + ClippedState(Image image, String url, int left, int top, int width, + int height) { + this.left = left; + this.top = top; + this.width = width; + this.height = height; + this.url = url; + image.setElement(impl.createStructure(url, left, top, width, height)); + image.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS); + fireSyntheticLoadEvent(image); + } + + private void fireSyntheticLoadEvent(final Image image) { + /* + * We need to synthesize a load event, because the native events that are + * fired would correspond to the loading of clear.cache.gif, which is + * incorrect. A native event would not even fire in Internet Explorer, + * because the root element is a wrapper element around the <img> element. + * Since we are synthesizing a load event, we do not need to sink the + * onload event. + * + * We use a deferred command here to simulate the native version of the + * load event as closely as possible. In the native event case, it is + * unlikely that a second load event would occur while you are in the load + * event handler. + */ + DeferredCommand.add(new Command() { + public void execute() { + if (image.loadListeners != null) { + image.loadListeners.fireLoad(image); + } + } + }); + } + + public int getHeight(Image image) { + return height; + } + + public int getOriginLeft() { + return left; + } + + public int getOriginTop() { + return top; + } + + public String getUrl(Image image) { + return url; + } + + public int getWidth(Image image) { + return width; + } + + public void setUrl(Image image, String url) { + image.changeState(new UnclippedState(image, url)); + } + + public void setUrlAndVisibleRect(Image image, String url, int left, + int top, int width, int height) { + if (!url.equals(url) || this.left != left || this.top != top + || this.width != width || this.height != height) { + + this.url = url; + this.left = left; + this.top = top; + this.width = width; + this.height = height; + + impl.adjust(image.getElement(), url, left, top, width, height); + fireSyntheticLoadEvent(image); + } + } + + public void setVisibleRect(Image image, int left, int top, int width, + int height) { + /* + * In the event that the clipping rectangle has not changed, we want to + * skip all of the work required with a getImpl().adjust, and we do not + * want to fire a load event. + */ + if (this.left != left || this.top != top || this.width != width + || this.height != height) { + + this.left = left; + this.top = top; + this.width = width; + this.height = height; + + impl.adjust(image.getElement(), url, left, top, width, height); + fireSyntheticLoadEvent(image); + } + } + + /* This method is used only by unit tests */ + protected String getStateName() { + return "clipped"; + } + } + + /** * This map is used to store prefetched images. If a reference is not kept to * the prefetched image objects, they can get garbage collected, which * sometimes keeps them from getting fully fetched. @@ -59,23 +286,48 @@ private LoadListenerCollection loadListeners; private MouseListenerCollection mouseListeners; + private State state; + /** * Creates an empty image. */ public Image() { - setElement(DOM.createImg()); - sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD | Event.ONERROR); + changeState(new UnclippedState(this)); setStyleName("gwt-Image"); } /** - * Creates an image with a specified URL. + * Creates an image with a specified URL. The load event will be fired once + * the image at the given URL has been retrieved by the browser. * * @param url the URL of the image to be displayed */ public Image(String url) { - this(); - setUrl(url); + changeState(new UnclippedState(this, url)); + setStyleName("gwt-Image"); + } + + /** + * Creates a clipped image with a specified URL and visibility rectangle. The + * visibility rectangle is declared relative to the the rectangle which + * encompasses the entire image, which has an upper-left vertex of (0,0). The + * load event will be fired immediately after the object has been constructed + * (i.e. potentially before the image has been loaded in the browser). Since + * the width and height are specified explicitly by the user, this behavior + * will not cause problems with retrieving the width and height of a clipped + * image in a load event handler. + * + * @param url the URL of the image to be displayed + * @param left the horizontal co-ordinate of the upper-left vertex of the + * visibility rectangle + * @param top the vertical co-ordinate of the upper-left vertex of the + * visibility rectangle + * @param width the width of the visibility rectangle + * @param height the height of the visibility rectangle + */ + public Image(String url, int left, int top, int width, int height) { + changeState(new ClippedState(this, url, left, top, width, height)); + setStyleName("gwt-Image"); } public void addClickListener(ClickListener listener) { @@ -100,12 +352,62 @@ } /** - * Gets the URL of the image. + * Gets the height of the image. When the image is in the unclipped state, the + * height of the image is not known until the image has been loaded (i.e. load + * event has been fired for the image). + * + * @return the height of the image, or 0 if the height is unknown + */ + public int getHeight() { + return state.getHeight(this); + } + + /** + * Gets the horizontal co-ordinate of the upper-left vertex of the image's + * visibility rectangle. If the image is in the unclipped state, then the + * visibility rectangle is assumed to be the rectangle which encompasses the + * entire image, which has an upper-left vertex of (0,0). + * + * @return the horizontal co-ordinate of the upper-left vertex of the image's + * visibility rectangle + */ + public int getOriginLeft() { + return state.getOriginLeft(); + } + + /** + * Gets the vertical co-ordinate of the upper-left vertex of the image's + * visibility rectangle. If the image is in the unclipped state, then the + * visibility rectangle is assumed to be the rectangle which encompasses the + * entire image, which has an upper-left vertex of (0,0). + * + * @return the vertical co-ordinate of the upper-left vertex of the image's + * visibility rectangle + */ + public int getOriginTop() { + return state.getOriginTop(); + } + + /** + * Gets the URL of the image. The URL that is returned is not necessarily the + * URL that was passed in by the user. It may have been transformed to an + * absolute URL. * * @return the image URL */ public String getUrl() { - return DOM.getAttribute(getElement(), "src"); + return state.getUrl(this); + } + + /** + * Gets the width of the image. When the image is in the unclipped state, the + * width of the image is not known until the image has been loaded (i.e. load + * event has been fired for the image). + * + * @return the width of the image, or 0 if the width is unknown + */ + public int getWidth() { + return state.getWidth(this); } public void onBrowserEvent(Event event) { @@ -160,11 +462,60 @@ } /** - * Sets the URL of the image to be displayed. + * Sets the URL of the image to be displayed. If the image is in the clipped + * state, a call to this method will cause a transition of the image to the + * unclipped state. Regardless of whether or not the image is in the clipped + * or unclipped state, a load event will be fired. * * @param url the image URL */ public void setUrl(String url) { - DOM.setAttribute(getElement(), "src", url); + state.setUrl(this, url); } -} \ No newline at end of file + + /** + * Sets the url and the visibility rectangle for the image at the same time. A + * single load event will be fired if either the incoming url or visiblity + * rectangle co-ordinates differ from the image's current url or current + * visibility rectangle co-ordinates. If the image is currently in the + * unclipped state, a call to this method will cause a transition to the + * clipped state. + * + * @param url the image URL + * @param left the horizontal coordinate of the upper-left vertex of the + * visibility rectangle + * @param top the vertical coordinate of the upper-left vertex of the + * visibility rectangle + * @param width the width of the visibility rectangle + * @param height the height of the visibility rectangle + */ + public void setUrlAndVisibleRect(String url, int left, int top, int width, + int height) { + state.setUrlAndVisibleRect(this, url, left, top, width, height); + } + + /** + * Sets the visibility rectangle of an image. The visibility rectangle is + * declared relative to the the rectangle which encompasses the entire image, + * which has an upper-left vertex of (0,0). Provided that any of the left, + * top, width, and height parameters are different than the those values that + * are currently set for the image, a load event will be fired. If the image + * is in the unclipped state, a call to this method will cause a transition of + * the image to the clipped state. This transition will cause a load event to + * fire. + * + * @param left the horizontal coordinate of the upper-left vertex of the + * visibility rectangle + * @param top the vertical coordinate of the upper-left vertex of the + * visibility rectangle + * @param width the width of the visibility rectangle + * @param height the height of the visibility rectangle + */ + public void setVisibleRect(int left, int top, int width, int height) { + state.setVisibleRect(this, left, top, width, height); + } + + private void changeState(State newState) { + state = newState; + } +}
diff --git a/user/src/com/google/gwt/user/client/ui/UIObject.java b/user/src/com/google/gwt/user/client/ui/UIObject.java index 0d4d4aa..f969059 100644 --- a/user/src/com/google/gwt/user/client/ui/UIObject.java +++ b/user/src/com/google/gwt/user/client/ui/UIObject.java
@@ -1,5 +1,5 @@ /* - * Copyright 2006 Google Inc. + * Copyright 2007 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 @@ -340,10 +340,27 @@ /** * Sets this object's browser element. UIObject subclasses must call this * method before attempting to call any other methods. + * + * If the browser element has already been set, then the current element's + * position is located in the DOM and removed. The new element is added into + * the previous element's position. * * @param elem the object's new element */ protected void setElement(Element elem) { + if (this.element != null) { + // replace this.element in its parent with elem. + replaceNode(this.element, elem); + } this.element = elem; } + + private native void replaceNode(Element node, Element newNode) /*-{ + var p = node.parentNode; + if (!p) { + return; + } + p.insertBefore(newNode, node); + p.removeChild(node); + }-*/; }
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java index b9f6005..2d4a2ee 100644 --- a/user/src/com/google/gwt/user/client/ui/Widget.java +++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -18,6 +18,7 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.EventListener; +import com.google.gwt.user.client.Element; /** * The base class for the majority of user-interface objects. Widget adds @@ -115,6 +116,34 @@ } /** + * Sets this object's browser element. Widget subclasses must call this + * method before attempting to call any other methods. + * + * If a browser element has already been attached, then it is replaced with + * the new element. The old event listeners are removed from the old browser + * element, and the event listeners are set up on the new browser element. + * + * @param elem the object's new element + */ + protected void setElement(Element elem) { + if (attached) { + // Remove old event listener to avoid leaking. onDetach will not do this + // for us, because it is only called the widget itself is detached from + // the document. + DOM.setEventListener(getElement(), null); + } + + super.setElement(elem); + + if (attached) { + // Hook the event listener back up on the new element. onAttach will not + // do this for us, because it is only called when the widget itself is + // attached to the document. + DOM.setEventListener(elem, this); + } + } + + /** * Gets the panel-defined layout data associated with this widget. * * @return the widget's layout data
diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java new file mode 100644 index 0000000..6d6f2cb --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
@@ -0,0 +1,56 @@ +/* + * Copyright 2007 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.user.client.ui.impl; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; + +/** + * Uses a combination of a clear image and a background image to clip all except + * a desired portion of an underlying image. + * + * Do not use this class - it is used for implementation only, and its methods + * may change in the future. + */ +public class ClippedImageImpl { + + public void adjust(Element img, String url, int left, int top, int width, + int height) { + String style = "url(" + url + ") no-repeat " + (-left + "px ") + + (-top + "px"); + DOM.setStyleAttribute(img, "background", style); + DOM.setStyleAttribute(img, "width", width + "px"); + DOM.setStyleAttribute(img, "height", height + "px"); + } + + public Element createStructure(String url, int left, int top, int width, + int height) { + Element tmp = DOM.createSpan(); + DOM.setInnerHTML(tmp, getHTML(url, left, top, width, height)); + return DOM.getFirstChild(tmp); + } + + public String getHTML(String url, int left, int top, int width, int height) { + String style = "width: " + width + "px; height: " + height + + "px; background: url(" + url + ") no-repeat " + (-left + "px ") + + (-top + "px"); + + String clippedImgHtml = "<img src='clear.cache.gif' style='" + + style + "' border='0'>"; + + return clippedImgHtml; + } +}
diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java new file mode 100644 index 0000000..d6f0fbb --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java
@@ -0,0 +1,68 @@ +/* + * Copyright 2007 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.user.client.ui.impl; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; + +/** + * Implements the clipped image as a IMG inside a custom tag because we can't + * use the IE PNG transparency filter on background-image images. + * + * Do not use this class - it is used for implementation only, and its methods + * may change in the future. + */ +public class ClippedImageImplIE6 extends ClippedImageImpl { + + public void adjust(Element clipper, String url, int left, int top, int width, + int height) { + + DOM.setStyleAttribute(clipper, "width", width + "px"); + DOM.setStyleAttribute(clipper, "height", height + "px"); + + // Update the nested image's url. + Element img = DOM.getFirstChild(clipper); + DOM.setStyleAttribute(img, "filter", + "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + + "',sizingMethod='crop')"); + DOM.setStyleAttribute(img, "marginLeft", -left + "px"); + DOM.setStyleAttribute(img, "marginTop", -top + "px"); + + // AlphaImageLoader requires that we size the image explicitly. + // It really only needs to be enough to show the revealed portion. + int imgWidth = left + width; + int imgHeight = top + height; + DOM.setIntAttribute(img, "width", imgWidth); + DOM.setIntAttribute(img, "height", imgHeight); + } + + public String getHTML(String url, int left, int top, int width, int height) { + String clipperStyle = "overflow: hidden; width: " + width + "px; height: " + + height + "px; padding: 0px"; + + String imgStyle = + "filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + + url + "',sizingMethod='crop'); margin-left: " + + -left + "px; margin-top: " + -top + "px" + "border: 0px"; + + String clippedImgHtml = "<gwt:clipper style=\"" + + clipperStyle + "\"><img src='clear.cache.gif' style=\"" + imgStyle + + "\" width=" + (left + width) + " height=" + (top + height) + + " border='0'></gwt:clipper>"; + + return clippedImgHtml; + } +}
diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImagePrototype.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImagePrototype.java new file mode 100644 index 0000000..159d00c --- /dev/null +++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImagePrototype.java
@@ -0,0 +1,57 @@ +/* + * Copyright 2007 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.user.client.ui.impl; + +import com.google.gwt.user.client.ui.Image; +import com.google.gwt.user.client.ui.AbstractImagePrototype; +import com.google.gwt.core.client.GWT; + +/** + * Implementation of {@link AbstractImagePrototype} for a clipped image. This + * class is used internally by the image bundle generator and is not intended + * for general use. It is subject to change without warning. + */ +public class ClippedImagePrototype extends AbstractImagePrototype { + + private static final ClippedImageImpl impl = (ClippedImageImpl) GWT.create(ClippedImageImpl.class); + + private int left = 0; + private int top = 0; + private int width = 0; + private int height = 0; + private String url = null; + + public ClippedImagePrototype(String url, int left, int top, int width, + int height) { + this.url = url; + this.left = left; + this.top = top; + this.width = width; + this.height = height; + } + + public void applyTo(Image image) { + image.setUrlAndVisibleRect(url, left, top, width, height); + } + + public Image createImage() { + return new Image(url, left, top, width, height); + } + + public String getHTML() { + return impl.getHTML(url, left, top, width, height); + } +}
diff --git a/user/src/com/google/gwt/user/public/clear.cache.gif b/user/src/com/google/gwt/user/public/clear.cache.gif new file mode 100644 index 0000000..e565824 --- /dev/null +++ b/user/src/com/google/gwt/user/public/clear.cache.gif Binary files differ