org.opencms.gwt.client.util.CmsPositionBean Maven / Gradle / Ivy
Show all versions of opencms-gwt Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.gwt.client.util;
import org.opencms.gwt.client.util.CmsDomUtil.Style;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.UIObject;
/**
* Bean holding the position data of a HTML DOM element.
*
* @since 8.0.0
*/
public class CmsPositionBean {
/** Position area. */
public static enum Area {
/** Bottom border. */
BORDER_BOTTOM,
/** Left border. */
BORDER_LEFT,
/** Right border. */
BORDER_RIGHT,
/** Top border. */
BORDER_TOP,
/** The center. */
CENTER,
/** Bottom left corner. */
CORNER_BOTTOM_LEFT,
/** Bottom right corner. */
CORNER_BOTTOM_RIGHT,
/** Top left corner. */
CORNER_TOP_LEFT,
/** Top right corner. */
CORNER_TOP_RIGHT
}
/** The directions. */
static enum Direction {
/** Bottom. */
bottom,
/** Left. */
left,
/** Right. */
right,
/** Top. */
top
}
/** Element height. */
private int m_height;
/** Position left. */
private int m_left;
/** Position top. */
private int m_top;
/** Element width. */
private int m_width;
/**
* Constructor.
*/
public CmsPositionBean() {
// default constructor
}
/**
* Copy constructor. Generating a copy of the given model.
*
* @param model the model to copy
*/
public CmsPositionBean(CmsPositionBean model) {
m_height = model.getHeight();
m_left = model.getLeft();
m_top = model.getTop();
m_width = model.getWidth();
}
/**
* Manipulates the position infos to ensure a minimum margin between the rectangles.
*
* @param posA the first position to check
* @param posB the second position to check
* @param margin the required margin
*/
public static void avoidCollision(CmsPositionBean posA, CmsPositionBean posB, int margin) {
Direction dir = null;
int diff = 0;
int diffTemp = (posB.getLeft() + posB.getWidth()) - posA.getLeft();
if (diffTemp > -margin) {
dir = Direction.left;
diff = diffTemp;
}
diffTemp = (posA.getLeft() + posA.getWidth()) - posB.getLeft();
if ((diffTemp > -margin) && (diffTemp < diff)) {
dir = Direction.right;
diff = diffTemp;
}
diffTemp = (posB.getTop() + posB.getHeight()) - posA.getTop();
if ((diffTemp > -margin) && (diffTemp < diff)) {
dir = Direction.top;
diff = diffTemp;
}
diffTemp = (posA.getTop() + posA.getHeight()) - posB.getTop();
if ((diffTemp > -margin) && (diffTemp < diff)) {
dir = Direction.bottom;
diff = diffTemp;
}
diff = (int)Math.ceil((1.0 * (diff + margin)) / 2);
if (dir != null) {
switch (dir) {
case left:
// move the left border of a
posA.setLeft(posA.getLeft() + diff);
posA.setWidth(posA.getWidth() - diff);
// move the right border of b
posB.setWidth(posB.getWidth() - diff);
break;
case right:
// move the left border of b
posB.setLeft(posB.getLeft() + diff);
posB.setWidth(posB.getWidth() - diff);
// move the right border of a
posA.setWidth(posA.getWidth() - diff);
break;
case top:
posA.setTop(posA.getTop() + diff);
posA.setHeight(posA.getHeight() - diff);
posB.setHeight(posB.getHeight() - diff);
break;
case bottom:
posB.setTop(posB.getTop() + diff);
posB.setHeight(posB.getHeight() - diff);
posA.setHeight(posA.getHeight() - diff);
break;
default:
// nothing to do
}
}
}
/**
* Checks whether the two position rectangles collide.
*
* @param posA the first position to check
* @param posB the second position to check
* @param margin the required margin
*
* @return true
if the two position rectangles collide
*/
public static boolean checkCollision(CmsPositionBean posA, CmsPositionBean posB, int margin) {
// check for non collision is easier
if ((posA.getLeft() - margin) >= (posB.getLeft() + posB.getWidth())) {
// posA is right of posB
return false;
}
if ((posA.getLeft() + posA.getWidth()) <= (posB.getLeft() - margin)) {
// posA is left of posB
return false;
}
if ((posA.getTop() - margin) >= (posB.getTop() + posB.getHeight())) {
// posA is bellow posB
return false;
}
if ((posA.getTop() + posA.getHeight()) <= (posB.getTop() - margin)) {
// posA is above posB
return false;
}
// in any other case the position rectangles collide
return true;
}
/**
* Collects the position information of the given UI object and returns a position info bean.
*
* @param element the object to read the position data from
*
* @return the position data
*/
public static CmsPositionBean generatePositionInfo(Element element) {
CmsPositionBean result = new CmsPositionBean();
result.setHeight(element.getOffsetHeight());
result.setWidth(element.getOffsetWidth());
result.setTop(element.getAbsoluteTop());
result.setLeft(element.getAbsoluteLeft());
return result;
}
/**
* Collects the position information of the given UI object and returns a position info bean.
*
* @param uiObject the object to read the position data from
*
* @return the position data
*/
public static CmsPositionBean generatePositionInfo(UIObject uiObject) {
return generatePositionInfo(uiObject.getElement());
}
/**
* Returns the bounding rectangle dimensions of the element including all floated elements.
*
* @param panel the panel
*
* @return the position info
*/
public static CmsPositionBean getBoundingClientRect(Element panel) {
return getBoundingClientRect(panel, true);
}
/**
* Returns the bounding rectangle dimensions of the element including all floated elements.
*
* @param panel the panel
* @param addScroll if true, the result will contain the coordinates in the document's coordinate system, not the viewport coordinate system
*
* @return the position info
*/
public static CmsPositionBean getBoundingClientRect(Element panel, boolean addScroll) {
CmsPositionBean result = new CmsPositionBean();
getBoundingClientRect(
panel,
result,
addScroll ? Window.getScrollLeft() : 0,
addScroll ? Window.getScrollTop() : 0);
return result;
}
/**
* Returns a position info representing the dimensions of all visible child elements of the given panel (excluding elements with position:absolute).
* If the panel has no visible child elements, it's outer dimensions are returned.
*
* @param panel the panel
*
* @return the position info
*/
public static CmsPositionBean getInnerDimensions(Element panel) {
boolean first = true;
int top = 0;
int left = 0;
int bottom = 0;
int right = 0;
Element child = panel.getFirstChildElement();
while (child != null) {
String tagName = child.getTagName();
if (tagName.equalsIgnoreCase("br")
|| tagName.equalsIgnoreCase("tr")
|| tagName.equalsIgnoreCase("thead")
|| tagName.equalsIgnoreCase("tfoot")
|| tagName.equalsIgnoreCase("script")
|| tagName.equalsIgnoreCase("style")) {
// ignore tags with no relevant position info
child = child.getNextSiblingElement();
continue;
}
String positioning = CmsDomUtil.getCurrentStyle(child, Style.position);
if (!Display.NONE.getCssName().equals(CmsDomUtil.getCurrentStyle(child, Style.display))
&& !(positioning.equalsIgnoreCase(Position.ABSOLUTE.getCssName())
|| positioning.equalsIgnoreCase(Position.FIXED.getCssName()))) {
CmsPositionBean childDimensions = getBoundingClientRect(child);
if (first) {
first = false;
top = childDimensions.getTop();
left = childDimensions.getLeft();
bottom = top + childDimensions.getHeight();
right = left + childDimensions.getWidth();
} else {
int wTop = childDimensions.getTop();
top = top < wTop ? top : wTop;
int wLeft = childDimensions.getLeft();
left = left < wLeft ? left : wLeft;
int wBottom = wTop + childDimensions.getHeight();
bottom = bottom > wBottom ? bottom : wBottom;
int wRight = wLeft + childDimensions.getWidth();
right = right > wRight ? right : wRight;
}
}
child = child.getNextSiblingElement();
}
if (!first) {
CmsPositionBean result = new CmsPositionBean();
result.setHeight(bottom - top);
result.setWidth(right - left);
result.setTop(top);
result.setLeft(left);
return result;
} else {
return getBoundingClientRect(panel);
}
}
/**
* Checks whether a value is in a given interval (including the end points).
*
* @param min the minimum of the interval
* @param max the maximum of the interval
* @param value the value to check
*
* @return true if the value is in the given interval
*/
public static boolean isInRangeInclusive(int min, int max, int value) {
return (min <= value) && (value <= max);
}
/**
* Uses the getBoundingClientRect method to evaluate the element dimensions.
*
* @param element the element
* @param pos the position bean
* @param scrollLeft the window scroll position left
* @param scrollTop the window scroll position top
*/
private static native void getBoundingClientRect(
Element element,
CmsPositionBean pos,
int scrollLeft,
int scrollTop)/*-{
var rect = element.getBoundingClientRect();
[email protected]::m_top=Math.round(rect.top+scrollTop);
[email protected]::m_left=Math.round(rect.left+scrollLeft);
[email protected]::m_height=Math.round(rect.height);
[email protected]::m_width=Math.round(rect.width);
}-*/;
/**
* Checks if the rectangle defined by this bean contains the given point.
*
* @param x the horizontal coordinate
* @param y the vertical coordinate
*
* @return true if this object contains the given point
*/
public boolean containsPoint(int x, int y) {
return isInRangeInclusive(getLeft(), (getLeft() + getWidth()) - 1, x)
&& isInRangeInclusive(getTop(), (getTop() + getHeight()) - 1, y);
}
/**
* Increases the dimensions to completely surround the child.
*
* @param child the child position info
* @param padding the padding to apply
*/
public void ensureSurrounds(CmsPositionBean child, int padding) {
// increase the size of the outer rectangle
if ((getLeft() + padding) > child.getLeft()) {
int diff = getLeft() - child.getLeft();
// ensure padding
diff += padding;
setLeft(getLeft() - diff);
setWidth(getWidth() + diff);
}
if ((getTop() + padding) > child.getTop()) {
int diff = getTop() - child.getTop();
diff += padding;
setTop(getTop() - diff);
setHeight(getHeight() + diff);
}
if ((getLeft() + getWidth()) < (child.getLeft() + child.getWidth() + padding)) {
int diff = (child.getLeft() + child.getWidth()) - (getLeft() + getWidth());
diff += padding;
setWidth(getWidth() + diff);
}
if ((getTop() + getHeight()) < (child.getTop() + child.getHeight() + padding)) {
int diff = (child.getTop() + child.getHeight()) - (getTop() + getHeight());
diff += padding;
setHeight(getHeight() + diff);
}
}
/**
* Returns over which area of this the given position is. Will return null
if the provided position is not within this position.
*
* @param absLeft the left position
* @param absTop the right position
* @param offset the border offset
*
* @return the area
*/
public Area getArea(int absLeft, int absTop, int offset) {
if (isOverElement(absLeft, absTop)) {
if (absLeft < (m_left + 10)) {
// left border
if (absTop < (m_top + offset)) {
// top left corner
return Area.CORNER_TOP_LEFT;
} else if (absTop > ((m_top + m_height) - offset)) {
// bottom left corner
return Area.CORNER_BOTTOM_LEFT;
}
return Area.BORDER_LEFT;
}
if (absLeft > ((m_left + m_width) - offset)) {
// right border
if (absTop < (m_top + offset)) {
// top right corner
return Area.CORNER_TOP_RIGHT;
// fixing opposite corner
} else if (absTop > ((m_top + m_height) - offset)) {
// bottom right corner
return Area.CORNER_BOTTOM_RIGHT;
// fixing opposite corner
}
return Area.BORDER_RIGHT;
}
if (absTop < (m_top + offset)) {
// border top
return Area.BORDER_TOP;
} else if (absTop > ((m_top + m_height) - offset)) {
// border bottom
return Area.BORDER_BOTTOM;
}
return Area.CENTER;
}
return null;
}
/**
* Returns the height.
*
* @return the height
*/
public int getHeight() {
return m_height;
}
/**
* Returns the left.
*
* @return the left
*/
public int getLeft() {
return m_left;
}
/**
* Returns the top.
*
* @return the top
*/
public int getTop() {
return m_top;
}
/**
* Returns the width.
*
* @return the width
*/
public int getWidth() {
return m_width;
}
/**
* Checks whether the given position is completely surrounded by this position.
*
* @param child the child position
* @param padding the padding to use
*
* @return true
if the child position is completely surrounded
*/
public boolean isInside(CmsPositionBean child, int padding) {
return ((getLeft() + padding) < child.getLeft()) // checking left border
&& ((getTop() + padding) < child.getTop()) // checking top border
&& (((getLeft() + getWidth()) - padding) > (child.getLeft() + child.getWidth())) // checking right border
&& (((getTop() + getHeight()) - padding) > (child.getTop() + child.getHeight())); // checking bottom border
}
/**
* Returns if given position is inside the position beans coordinates.
*
* @param absLeft the absolute left position
* @param absTop the absolute top position
*
* @return true if the given position if within the beans coordinates
*/
public boolean isOverElement(int absLeft, int absTop) {
if ((absTop > m_top) && (absTop < (m_top + m_height)) && (absLeft > m_left) && (absLeft < (m_left + m_width))) {
return true;
}
/* */
return false;
}
/**
* Returns if given absolute top is above the vertical middle of the position beans coordinates.
*
* @param absTop the absolute top position
* @return true if given absolute top is above the vertical middle
*/
public boolean isOverTopHalf(int absTop) {
if (absTop < (m_top + (m_height / 2))) {
return true;
}
return false;
}
/**
* Sets the height.
*
* @param height the height to set
*/
public void setHeight(int height) {
m_height = height;
}
/**
* Sets the left.
*
* @param left the left to set
*/
public void setLeft(int left) {
m_left = left;
}
/**
* Sets the top.
*
* @param top the top to set
*/
public void setTop(int top) {
m_top = top;
}
/**
* Sets the width.
*
* @param width the width to set
*/
public void setWidth(int width) {
m_width = width;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "top: " + m_top + " left: " + m_left + " height: " + m_height + " width: " + m_width;
}
}