Fixes Issue #286.
Mouse event coordinates have been unreliable under various scrolling
scenarios. This patch addresses the various issues that were
responsible. First, it fixes a bug with getAbsoluteTop/Left for
DOMImplStandard and DOMImplSafari where scrolling was not full
accounted for due to a walk of the offsetParent not the parentNode.
Secondly, it adds the appropriate scroll offset for body and the
current element for MouseListenerCollection and MouseWheelCollection.
Thirdly, it changes DOM.eventGetClientX/Y to rely on pageX/Y for
DOMImplSafari since Safari2 returns the wrong value for clientX/Y
(This also makes the solution work on hosted mode's WebKit and WebKit
tip where the clientX/Y bug has been fixed).
Patch by: knorton, fredsa
Review by: jgw, ecc
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1005 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImpl.java b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
index 06036ef..4069ab9 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
@@ -137,20 +137,32 @@
public native int getAbsoluteLeft(Element elem) /*-{
var left = 0;
+ var curr = elem;
+ // This intentionally excludes body which has a null offsetParent.
+ while (curr.offsetParent) {
+ left -= curr.scrollLeft;
+ curr = curr.parentNode;
+ }
while (elem) {
- left += elem.offsetLeft - elem.scrollLeft;
+ left += elem.offsetLeft;
elem = elem.offsetParent;
}
- return left + $doc.body.scrollLeft;
+ return left;
}-*/;
public native int getAbsoluteTop(Element elem) /*-{
var top = 0;
+ var curr = elem;
+ // This intentionally excludes body which has a null offsetParent.
+ while (curr.offsetParent) {
+ top -= curr.scrollTop;
+ curr = curr.parentNode;
+ }
while (elem) {
- top += elem.offsetTop - elem.scrollTop;
+ top += elem.offsetTop;
elem = elem.offsetParent;
}
- return top + $doc.body.scrollTop;
+ return top;
}-*/;
public abstract Element getChild(Element elem, int index);
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java b/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
index 0266024..dd452a5 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
@@ -22,6 +22,15 @@
* Safari implementation of {@link com.google.gwt.user.client.impl.DOMImpl}.
*/
class DOMImplSafari extends DOMImplStandard {
+ public native int eventGetClientX(Event evt) /*-{
+ // In Safari2: clientX is wrong and pageX is returned instead.
+ return evt.pageX - $doc.body.scrollLeft;
+ }-*/;
+
+ public native int eventGetClientY(Event evt) /*-{
+ // In Safari2: clientY is wrong and pageY is returned instead.
+ return evt.pageY - $doc.body.scrollTop;
+ }-*/;
public native int eventGetMouseWheelVelocityY(Event evt) /*-{
return -evt.wheelDelta / 40;
@@ -29,37 +38,49 @@
public native int getAbsoluteLeft(Element elem) /*-{
var left = 0;
+ var curr = elem;
+ // This intentionally excludes body which has a null offsetParent.
+ while (curr.offsetParent) {
+ left -= curr.scrollLeft;
+ curr = curr.parentNode;
+ }
while (elem) {
- left += elem.offsetLeft - elem.scrollLeft;
+ left += elem.offsetLeft;
// Safari bug: a top-level absolutely positioned element includes the
// body's offset position already.
var parent = elem.offsetParent;
if (parent && (parent.tagName == 'BODY') &&
(elem.style.position == 'absolute')) {
- return left;
+ break;
}
elem = parent;
}
- return left + $doc.body.scrollLeft;
+ return left;
}-*/;
public native int getAbsoluteTop(Element elem) /*-{
var top = 0;
+ var curr = elem;
+ // This intentionally excludes body which has a null offsetParent.
+ while (curr.offsetParent) {
+ top -= curr.scrollTop;
+ curr = curr.parentNode;
+ }
while (elem) {
- top += elem.offsetTop - elem.scrollTop;
+ top += elem.offsetTop;
// Safari bug: a top-level absolutely positioned element includes the
// body's offset position already.
var parent = elem.offsetParent;
if (parent && (parent.tagName == 'BODY') &&
(elem.style.position == 'absolute')) {
- return top;
+ break;
}
elem = parent;
}
- return top + $doc.body.scrollTop;
+ return top;
}-*/;
}
diff --git a/user/src/com/google/gwt/user/client/ui/MouseListenerCollection.java b/user/src/com/google/gwt/user/client/ui/MouseListenerCollection.java
index 4d57c7f..044ace3 100644
--- a/user/src/com/google/gwt/user/client/ui/MouseListenerCollection.java
+++ b/user/src/com/google/gwt/user/client/ui/MouseListenerCollection.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
@@ -18,6 +18,7 @@
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Window;
import java.util.Iterator;
import java.util.Vector;
@@ -62,10 +63,15 @@
* @param event the {@link Event} received by the widget
*/
public void fireMouseEvent(Widget sender, Event event) {
+ final Element senderElem = sender.getElement();
int x = DOM.eventGetClientX(event)
- - DOM.getAbsoluteLeft(sender.getElement());
+ - DOM.getAbsoluteLeft(sender.getElement())
+ + DOM.getElementPropertyInt(senderElem, "scrollLeft")
+ + Window.getScrollLeft();
int y = DOM.eventGetClientY(event)
- - DOM.getAbsoluteTop(sender.getElement());
+ - DOM.getAbsoluteTop(sender.getElement())
+ + DOM.getElementPropertyInt(senderElem, "scrollTop")
+ + Window.getScrollTop();
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEDOWN:
diff --git a/user/src/com/google/gwt/user/client/ui/MouseWheelListenerCollection.java b/user/src/com/google/gwt/user/client/ui/MouseWheelListenerCollection.java
index 1cb2906..0973196 100644
--- a/user/src/com/google/gwt/user/client/ui/MouseWheelListenerCollection.java
+++ b/user/src/com/google/gwt/user/client/ui/MouseWheelListenerCollection.java
@@ -16,7 +16,9 @@
package com.google.gwt.user.client.ui;
import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Window;
import java.util.Iterator;
import java.util.Vector;
@@ -53,10 +55,15 @@
*/
public void fireMouseWheelEvent(Widget sender, Event event) {
if (DOM.eventGetType(event) == Event.ONMOUSEWHEEL) {
+ final Element senderElem = sender.getElement();
int x = DOM.eventGetClientX(event)
- - DOM.getAbsoluteLeft(sender.getElement());
+ - DOM.getAbsoluteLeft(sender.getElement())
+ + DOM.getElementPropertyInt(senderElem, "scrollLeft")
+ + Window.getScrollLeft();
int y = DOM.eventGetClientY(event)
- - DOM.getAbsoluteTop(sender.getElement());
+ - DOM.getAbsoluteTop(sender.getElement())
+ + DOM.getElementPropertyInt(senderElem, "scrollTop")
+ + Window.getScrollTop();
MouseWheelVelocity velocity = new MouseWheelVelocity(event);
fireMouseWheel(sender, x, y, velocity);