| /* | |
| * 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.layout.client; | |
| import com.google.gwt.dom.client.Element; | |
| import com.google.gwt.dom.client.Node; | |
| import com.google.gwt.dom.client.NodeList; | |
| import com.google.gwt.dom.client.Style; | |
| import com.google.gwt.dom.client.Style.Display; | |
| import com.google.gwt.dom.client.Style.Unit; | |
| import com.google.gwt.layout.client.Layout.Layer; | |
| /** | |
| * This implementation is used on IE8. Unlike {@code LayoutImpl}, it converts | |
| * all values to pixels before setting them. This is necessary because this | |
| * browser incorrectly calculates the relative sizes and positions of CSS | |
| * properties specified in certain units (e.g., when the value of an 'em' is | |
| * non-integral in pixels). | |
| */ | |
| public class LayoutImplIE8 extends LayoutImpl { | |
| private static native Layer getLayer(Element container) /*-{ | |
| return container.__layer; | |
| }-*/; | |
| private static native void setLayer(Element container, Layer layer) /*-{ | |
| // Potential leak: This is cleaned up in detach(). | |
| container.__layer = layer; | |
| }-*/; | |
| @Override | |
| public void layout(Layer layer) { | |
| Style style = layer.container.getStyle(); | |
| setLayer(layer.container, layer); | |
| // Whenever the visibility of a layer changes, we need to ensure that | |
| // layout() is run again for it. This is because the translation of units | |
| // to pixel values will be incorrect for invisible elements, and thus must | |
| // be fixed when they become visible. | |
| if (layer.visible) { | |
| String oldDisplay = style.getDisplay(); | |
| style.clearDisplay(); | |
| // We control the layer element, so assume that any non-zero display | |
| // property means it was set to 'none'. | |
| if (oldDisplay.length() > 0) { | |
| updateVisibility(layer.container); | |
| } | |
| } else { | |
| style.setDisplay(Display.NONE); | |
| } | |
| if (layer.setLeft) { | |
| setValue(layer, "left", layer.left, layer.leftUnit, false, false); | |
| } else { | |
| style.clearLeft(); | |
| } | |
| if (layer.setRight) { | |
| setValue(layer, "right", layer.right, layer.rightUnit, false, false); | |
| } else { | |
| style.clearRight(); | |
| } | |
| if (layer.setTop) { | |
| setValue(layer, "top", layer.top, layer.topUnit, true, false); | |
| } else { | |
| style.clearTop(); | |
| } | |
| if (layer.setBottom) { | |
| setValue(layer, "bottom", layer.bottom, layer.bottomUnit, true, false); | |
| } else { | |
| style.clearBottom(); | |
| } | |
| if (layer.setWidth) { | |
| setValue(layer, "width", layer.width, layer.widthUnit, false, true); | |
| } else { | |
| style.clearWidth(); | |
| } | |
| if (layer.setHeight) { | |
| setValue(layer, "height", layer.height, layer.heightUnit, true, true); | |
| } else { | |
| style.clearHeight(); | |
| } | |
| style = layer.child.getStyle(); | |
| switch (layer.hPos) { | |
| case BEGIN: | |
| style.setLeft(0, Unit.PX); | |
| style.clearRight(); | |
| break; | |
| case END: | |
| style.clearLeft(); | |
| style.setRight(0, Unit.PX); | |
| break; | |
| case STRETCH: | |
| style.setLeft(0, Unit.PX); | |
| style.setRight(0, Unit.PX); | |
| break; | |
| } | |
| switch (layer.vPos) { | |
| case BEGIN: | |
| style.setTop(0, Unit.PX); | |
| style.clearBottom(); | |
| break; | |
| case END: | |
| style.clearTop(); | |
| style.setBottom(0, Unit.PX); | |
| break; | |
| case STRETCH: | |
| style.setTop(0, Unit.PX); | |
| style.setBottom(0, Unit.PX); | |
| break; | |
| } | |
| } | |
| @Override | |
| public void onDetach(Element parent) { | |
| removeLayerRefs(parent); | |
| } | |
| private native void removeLayerRefs(Element parent) /*-{ | |
| for (var i = 0; i < parent.childNodes.length; ++i) { | |
| var container = parent.childNodes[i]; | |
| if (container.__layer) { | |
| container.__layer = null; | |
| } | |
| } | |
| }-*/; | |
| private void setValue(Layer layer, String prop, double value, Unit unit, | |
| boolean vertical, boolean noNegative) { | |
| switch (unit) { | |
| case PX: | |
| case PCT: | |
| // Leave PX and PCT alone. PX doesn't need to be translated, and PCT | |
| // can't be. | |
| break; | |
| default: | |
| value = value * getUnitSizeInPixels(layer.container, unit, vertical); | |
| value = (int) (value + 0.5); // Round to the nearest pixel. | |
| unit = Unit.PX; | |
| break; | |
| } | |
| if (noNegative) { | |
| if (value < 0) { | |
| value = 0; | |
| } | |
| } | |
| layer.getContainerElement().getStyle().setProperty(prop, value, unit); | |
| } | |
| private void updateVisibility(Element container) { | |
| // If this element has an associated layer, re-run layout for it. | |
| Layer layer = getLayer(container); | |
| if (layer != null) { | |
| layout(layer); | |
| } | |
| // Walk all children, looking for elements with a '__layer' property. If one | |
| // exists, call layout() for that element. This is not cheap, but it's the | |
| // only way to correctly ensure that layout units get translated correctly. | |
| NodeList<Node> nodes = container.getChildNodes(); | |
| for (int i = 0; i < nodes.getLength(); ++i) { | |
| Node node = nodes.getItem(i); | |
| if (node.getNodeType() == Node.ELEMENT_NODE) { | |
| updateVisibility(node.<Element>cast()); | |
| } | |
| } | |
| } | |
| } |