All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.layout.client.LayoutImplIE6 Maven / Gradle / Ivy

There is a newer version: 2.10.0
Show newest version
/*
 * 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 static com.google.gwt.dom.client.Style.Unit.PX;

import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.layout.client.Layout.Layer;
import com.google.gwt.user.client.Window;

/**
 * IE6-specific implementation, which uses the "onresize" event, along with a
 * series of measurement tools, to deal with several IE6 bugs. Specifically,
 * IE6 doesn't support simultaneous left-right and top-bottom values, and it
 * misplaces by one pixel elements whose right/bottom properties are set.
 * 
 * Because this implementation gets compiled in for both IE6 and 7, it
 * dynamically detects IE7 and punts to the super implementation.
 */
class LayoutImplIE6 extends LayoutImplIE8 {

  private static Element createStyleRuler(Element parent) {
    DivElement styleRuler = Document.get().createDivElement();
    DivElement styleInner = Document.get().createDivElement();

    styleRuler.getStyle().setPosition(Position.ABSOLUTE);
    styleRuler.getStyle().setLeft(-10000, PX);

    parent.appendChild(styleRuler);
    styleRuler.appendChild(styleInner);
    return styleRuler;
  }

  private static void hookWindowResize(final Element elem) {
    Window.addResizeHandler(new ResizeHandler() {
      public void onResize(ResizeEvent event) {
        resizeRelativeToParent(elem);
      }
    });
  }

  private static native void measureDecoration(Element elem) /*-{
    var ruler = elem.__styleRuler;
    var inner = ruler.children[0];
    var s = inner.style, cs = elem.currentStyle;

    s.borderLeftStyle = cs.borderLeftStyle;
    s.borderRightStyle = cs.borderRightStyle;
    s.borderTopStyle = cs.borderTopStyle;
    s.borderBottomStyle = cs.borderBottomStyle;

    s.borderLeftWidth = cs.borderLeftWidth;
    s.borderRightWidth = cs.borderRightWidth;
    s.borderTopWidth = cs.borderTopWidth;
    s.borderBottomWidth = cs.borderBottomWidth;

    // Oddly enough, allowing the word 'auto' to creep into the style
    // ruler's margin causes it to take up all of its parent's space.
    s.marginLeft = (cs.marginLeft == 'auto') ? '' : cs.marginLeft;
    s.marginRight = (cs.marginRight == 'auto') ? '' : cs.marginRight;
    s.marginTop = (cs.marginTop == 'auto') ? '' : cs.marginTop;
    s.marginBottom = (cs.marginBottom == 'auto') ? '' : cs.marginBottom;

    s.paddingLeft = cs.paddingLeft;
    s.paddingRight = cs.paddingRight;
    s.paddingTop = cs.paddingTop;
    s.paddingBottom = cs.paddingBottom;

    s.width = s.height = 32;
    elem.__decoWidth = ruler.offsetWidth - 32;
    elem.__decoHeight = ruler.offsetHeight - 32;
  }-*/;

  private static native void resizeRelativeToParent(Element elem) /*-{
    var parent = elem.__resizeParent;
    if (parent) {
      @com.google.gwt.layout.client.LayoutImplIE6::measureDecoration(Lcom/google/gwt/dom/client/Element;)(elem);
      elem.style.left = 0;
      elem.style.top = 0;
      elem.style.width = parent.clientWidth - elem.__decoWidth;
      elem.style.height = parent.clientHeight - elem.__decoHeight;
    }
  }-*/;

  private static native void setLayer(Element container, Layer layer) /*-{
    // Potential leak: This is cleaned up in detach().
    container.__layer = layer;
  }-*/;

  private static native void setPropertyElement(Element elem, String name,
      Element value) /*-{
    elem[name] = value;
  }-*/;

  @Override
  public Element attachChild(Element parent, Element child, Element before) {
    DivElement container = Document.get().createDivElement();
    container.appendChild(child);

    container.getStyle().setPosition(Position.ABSOLUTE);
    container.getStyle().setOverflow(Overflow.HIDDEN);
    child.getStyle().setPosition(Position.ABSOLUTE);

    // Hang the style ruler from the container element, but associate it with
    // the child element, so that measureDecoration(child) will work.
    setPropertyElement(child, "__styleRuler", createStyleRuler(container));

    Element beforeContainer = null;
    if (before != null) {
      beforeContainer = before.getParentElement();
      assert beforeContainer.getParentElement() == parent : "Element to insert before must be a sibling";
    }
    parent.insertBefore(container, beforeContainer);
    return container;
  }

  @Override
  public void fillParent(Element elem) {
    fillParentImpl(elem);
  }

  @Override
  public void finalizeLayout(Element parent) {
    resizeRelativeToParent(parent);
    resizeHandler(parent, true);
  }

  @Override
  public void initParent(Element parent) {
    super.initParent(parent);
    setPropertyElement(parent, "__styleRuler", createStyleRuler(parent));
  }

  @Override
  public void layout(Layer layer) {
    Element elem = layer.container;
    Style style = elem.getStyle();

    if (layer.visible) {
      style.clearDisplay();
    } else {
      style.setDisplay(Display.NONE);
    }

    setLayer(elem, layer);
  }

  @Override
  public void onAttach(Element parent) {
    // No need to re-connect layer refs. This will be taken care of
    // automatically in layout().
    initResizeHandler(parent);
    initUnitChangeHandler(parent, relativeRuler);
  }

  @Override
  public void onDetach(Element parent) {
    removeLayerRefs(parent);
    removeResizeHandler(parent);
    removeUnitChangeHandler(relativeRuler);
  }

  private native void fillParentImpl(Element elem) /*-{
    // Hook the parent element's onresize event. If the parent is the ,
    // then we have to go through the Window class to get the resize event,
    // because IE6 requires a bunch of foul hackery to safely hook it.
    var parent = elem.parentElement;
    if (parent.tagName.toLowerCase() == 'body') {
      elem.style.position = 'absolute';
      var docElem = parent.parentElement;
      elem.__resizeParent = docElem;
      @com.google.gwt.layout.client.LayoutImplIE6::hookWindowResize(Lcom/google/gwt/dom/client/Element;)(elem);
      return;
    }

    function resize() {
      @com.google.gwt.layout.client.LayoutImplIE6::resizeRelativeToParent(Lcom/google/gwt/dom/client/Element;)(elem);
    }

    elem.__resizeParent = parent;
    parent.onresize = resize;
    resize();
  }-*/;

  /**
   * This does not call $entry() because no user code is reachable from
   * resizeHandler.
   */
  private native void initResizeHandler(Element parent) /*-{
    // Potential leak: This is cleaned up in detach().
    var self = this;
    parent.onresize = function() {
      [email protected]::resizeHandler(Lcom/google/gwt/dom/client/Element;)(parent);
    };
  }-*/;

  /**
   * This does not call $entry() because no user code is reachable from
   * resizeHandler.
   */
  private native void initUnitChangeHandler(Element parent, Element ruler) /*-{
    // Potential leak: This is cleaned up in detach().
    var self = this;
    ruler.onresize = function() {
      [email protected]::resizeHandler(Lcom/google/gwt/dom/client/Element;Z)(parent, true);
    };
  }-*/;

  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 native void removeResizeHandler(Element parent) /*-{
    parent.onresize = null;
  }-*/;

  private native void removeUnitChangeHandler(Element ruler) /*-{
    ruler.onresize = null;
  }-*/;

  private void resizeHandler(Element parent) {
    resizeHandler(parent, false);
  }
  
  private native void resizeHandler(Element parent, boolean force) /*-{
    if (!force &&
        ((parent.offsetWidth == parent.__oldWidth) &&
         (parent.offsetHeight == parent.__oldHeight))) {
      return;
    }
    parent.__oldWidth = parent.offsetWidth;
    parent.__oldHeight = parent.offsetHeight;

    var parentWidth = parent.clientWidth;
    var parentHeight = parent.clientHeight;

    // Iterate over the children, assuming each of them is an unstyled
    // container element.
    for (var i = 0; i < parent.childNodes.length; ++i) {
      // Don't attempt to layout the rulers.
      var container = parent.childNodes[i];
      var layer = container.__layer;
      if (!layer) {
        continue;
      }

      // This is ugly, but it's less ugly than writing all these JSNI refs inline.
      var _setLeft = [email protected]::setLeft;
      var _setTop = [email protected]::setTop;
      var _setWidth = [email protected]::setWidth;
      var _setHeight = [email protected]::setHeight;
      var _setRight = [email protected]::setRight;
      var _setBottom = [email protected]::setBottom;

      var _left = [email protected]::left;
      var _top = [email protected]::top;
      var _width = [email protected]::width;
      var _height = [email protected]::height;
      var _right = [email protected]::right;
      var _bottom = [email protected]::bottom;

      var _leftUnit = [email protected]::leftUnit;
      var _topUnit = [email protected]::topUnit;
      var _widthUnit = [email protected]::widthUnit;
      var _heightUnit = [email protected]::heightUnit;
      var _rightUnit = [email protected]::rightUnit;
      var _bottomUnit = [email protected]::bottomUnit;

      var _hPos = [email protected]::hPos
                  [email protected]::ordinal()();
      var _vPos = [email protected]::vPos
                  [email protected]::ordinal()();

      // Apply the requested position & size values to the element's style.
      var style = container.style;
      style.left = _setLeft ? (_left + [email protected]::getType()()) : "";
      style.top = _setTop ? (_top + [email protected]::getType()()) : "";
      style.width = _setWidth ? (_width + [email protected]::getType()()) : "";
      style.height = _setHeight ? (_height + [email protected]::getType()()) : "";

      // If right is defined, reposition/size the container horizontally.
      if (_setRight) {
        var ratio = [email protected]::getUnitSizeInPixels(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/dom/client/Style$Unit;Z)(parent, _rightUnit, false);
        var right = parentWidth - (_right * ratio);

        if (!_setLeft) {
          // There's no left defined; adjust the left position move the element to the right edge.
          container.style.left = (right - container.offsetWidth) + 'px';
        } else {
          // Both left and right are defined; calculate the width and set it.
          var leftRatio = [email protected]::getUnitSizeInPixels(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/dom/client/Style$Unit;Z)(parent, _leftUnit, false);
          var left = (_left * leftRatio);
          if (right > left) {
            container.style.width = (right - left) + 'px';
          }
        }
      }

      // If bottom is defined, reposition/size the container vertically.
      if (_setBottom) {
        var ratio = [email protected]::getUnitSizeInPixels(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/dom/client/Style$Unit;Z)(parent, _bottomUnit, true);
        var bottom = parentHeight - (_bottom * ratio);

        if (!_setTop) {
          // There's no top defined; adjust the left position move the element to the bottom edge.
          container.style.top = (bottom - container.offsetHeight) + 'px';
        } else {
          // Both top and bottom are defined; calculate the height and set it.
          var topRatio = [email protected]::getUnitSizeInPixels(Lcom/google/gwt/dom/client/Element;Lcom/google/gwt/dom/client/Style$Unit;Z)(parent, _topUnit, true);
          var top = (_top * topRatio);
          if (bottom > top) {
            container.style.height = (bottom - top) + 'px';
          }
        }
      }

      // Resize and position the child based on the layer's [hv]Pos.
      var child = container.firstChild;
      @com.google.gwt.layout.client.LayoutImplIE6::measureDecoration(Lcom/google/gwt/dom/client/Element;)(child);
      var childDecoWidth = child.__decoWidth;
      var childDecoHeight = child.__decoHeight;

      if (container.offsetWidth > childDecoWidth) {
        switch (_hPos) {
          case 0: // BEGIN
            child.style.left = '0px';
            break;
          case 1: // END
            child.style.left = (container.offsetWidth - childDecoWidth - child.offsetWidth) + 'px';
            break;
          case 2: // STRETCH
            child.style.left = '0px';
            child.style.width = (container.offsetWidth - childDecoWidth) + 'px';
            break;
        }
      }
      if (container.offsetHeight > childDecoHeight) {
        switch (_vPos) {
          case 0: // BEGIN
            child.style.top = '0px';
            break;
          case 1: // END
            child.style.top = (container.offsetHeight - childDecoHeight - child.offsetHeight) + 'px';
            break;
          case 2: // STRETCH
            child.style.top = '0px';
            child.style.height = (container.offsetHeight - childDecoHeight) + 'px';
            break;
        }
      }
    }
  }-*/;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy