svn changeset:11103/svn branch:6.3_ddtags/6.7.0.beta1
@@ -37,6 +37,25 @@ public class Util { | |||
debugger; | |||
}-*/; | |||
/** | |||
* | |||
* Returns the topmost element of from given coordinates. | |||
* | |||
* TODO fix crossplat issues clientX vs pageX. See quircksmode | |||
* | |||
* @param x | |||
* @param y | |||
* @return the element at given coordinates | |||
*/ | |||
public static native Element getElementFromPoint(int clientX, int clientY) | |||
/*-{ | |||
var el = $wnd.document.elementFromPoint(clientX, clientY); | |||
if(el.nodeType == 3) { | |||
el = el.parentNode; | |||
} | |||
return el; | |||
}-*/; | |||
private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400; | |||
private static Set<Paintable> latelyChangedWidgets = new HashSet<Paintable>(); | |||
@@ -11,6 +11,7 @@ import com.google.gwt.user.client.DeferredCommand; | |||
import com.google.gwt.user.client.Element; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.BrowserInfo; | |||
import com.vaadin.terminal.gwt.client.Container; | |||
import com.vaadin.terminal.gwt.client.MouseEventDetails; | |||
import com.vaadin.terminal.gwt.client.Paintable; | |||
@@ -44,11 +45,7 @@ public class VDragDropPane extends VAbsoluteLayout implements Container, | |||
transferable.setComponent(paintable); | |||
VDragEvent drag = VDragAndDropManager.get().startDrag( | |||
transferable, event.getNativeEvent(), true); | |||
Element cloneNode = (Element) ((Widget) paintable).getElement() | |||
.cloneNode(true); | |||
cloneNode.getStyle().setBackgroundColor("#999"); | |||
cloneNode.getStyle().setOpacity(0.4); | |||
drag.setDragImage(cloneNode); | |||
drag.createDragImage(((Widget) paintable).getElement(), true); | |||
drag.getEventDetails().put( | |||
"mouseDown", | |||
new MouseEventDetails(event.getNativeEvent()) | |||
@@ -57,7 +54,10 @@ public class VDragDropPane extends VAbsoluteLayout implements Container, | |||
} | |||
}, MouseDownEvent.getType()); | |||
hookHtml5Events(getElement()); | |||
if (!BrowserInfo.get().isIE()) { | |||
// TODO make this IE compatible | |||
hookHtml5Events(getElement()); | |||
} | |||
getStyleElement().getStyle().setBackgroundColor("yellow"); | |||
} | |||
@@ -184,6 +184,12 @@ public class VDragDropPane extends VAbsoluteLayout implements Container, | |||
if (dropHandler == null) { | |||
dropHandler = new VAbstractDropHandler() { | |||
@Override | |||
public void dragEnter(VDragEvent drag) { | |||
ApplicationConnection.getConsole().log("DDPane DragEnter"); | |||
super.dragEnter(drag); | |||
} | |||
@Override | |||
public Paintable getPaintable() { | |||
return VDragDropPane.this; |
@@ -2492,6 +2492,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler { | |||
if (dragmode != 0) { | |||
mDown = true; | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
} | |||
break; | |||
case Event.ONMOUSEOUT: | |||
@@ -2506,11 +2507,12 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler { | |||
// TODO propertyId | |||
VDragEvent ev = VDragAndDropManager.get() | |||
.startDrag(transferable, event, true); | |||
Element cloneNode = (Element) getElement() | |||
.cloneNode(true); | |||
cloneNode.getStyle().setOpacity(0.4); | |||
ev.setDragImage(cloneNode); | |||
ev.createDragImage(getElement(), true); | |||
mDown = false; | |||
// prevent text selection | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
} | |||
default: | |||
break; |
@@ -11,11 +11,10 @@ import java.util.Set; | |||
import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.dom.client.Style; | |||
import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.DeferredCommand; | |||
import com.google.gwt.user.client.Element; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.EventListener; | |||
import com.google.gwt.user.client.Window; | |||
import com.google.gwt.user.client.ui.FlowPanel; | |||
import com.google.gwt.user.client.ui.SimplePanel; | |||
@@ -161,7 +160,10 @@ public class VTree extends FlowPanel implements Paintable, VHasDropHandler { | |||
} | |||
private void updateTreeRelatedTransferData(VDragEvent drag) { | |||
private void updateTreeRelatedDragData(VDragEvent drag) { | |||
currentMouseOverKey = findCurrentMouseOverKey(drag.getElementOver()); | |||
drag.getEventDetails().put("itemIdOver", currentMouseOverKey); | |||
if (currentMouseOverKey != null) { | |||
@@ -177,13 +179,36 @@ public class VTree extends FlowPanel implements Paintable, VHasDropHandler { | |||
} | |||
} | |||
private String findCurrentMouseOverKey(Element elementOver) { | |||
TreeNode treeNode = null; | |||
Element curEl = elementOver; | |||
while (curEl != null) { | |||
try { | |||
EventListener eventListener = Event.getEventListener(curEl); | |||
if (eventListener != null) { | |||
// found a widget | |||
if (eventListener instanceof TreeNode) { | |||
treeNode = (TreeNode) eventListener; | |||
} | |||
break; | |||
} else { | |||
curEl = (Element) curEl.getParentElement(); | |||
} | |||
} catch (Exception e) { | |||
ApplicationConnection.getConsole().log(e.getMessage()); | |||
e.printStackTrace(); | |||
} | |||
} | |||
return treeNode == null ? null : treeNode.key; | |||
} | |||
private void updateDropHandler(UIDL childUidl) { | |||
if (dropHandler == null) { | |||
dropHandler = new VAbstractDropHandler() { | |||
@Override | |||
public void dragEnter(VDragEvent drag) { | |||
updateTreeRelatedTransferData(drag); | |||
updateTreeRelatedDragData(drag); | |||
super.dragEnter(drag); | |||
} | |||
@@ -197,59 +222,47 @@ public class VTree extends FlowPanel implements Paintable, VHasDropHandler { | |||
"itemIdOver"); | |||
final String oldDetail = (String) currentDrag | |||
.getEventDetails().get("detail"); | |||
/* | |||
* Using deferred command, so event bubbles to TreeNode | |||
* event listener. Currently here via preview | |||
*/ | |||
DeferredCommand.addCommand(new Command() { | |||
public void execute() { | |||
final String detail = getDropDetail(currentDrag | |||
.getCurrentGwtEvent()); | |||
boolean nodeHasChanged = (currentMouseOverKey != null && currentMouseOverKey != oldIdOver) | |||
|| (oldIdOver != null); | |||
boolean detailHasChanded = !detail | |||
.equals(oldDetail); | |||
if (nodeHasChanged || detailHasChanded) { | |||
ApplicationConnection.getConsole().log( | |||
"Change in Transferable " | |||
+ currentMouseOverKey + " " | |||
+ detail); | |||
updateTreeRelatedTransferData(currentDrag); | |||
VAcceptCallback accpectedCb = new VAcceptCallback() { | |||
public void handleResponse( | |||
ValueMap responseData) { | |||
if (responseData == null // via client | |||
// side | |||
// validation | |||
|| responseData | |||
.containsKey("accepted")) { | |||
keyToNode.get(currentMouseOverKey) | |||
.emphasis(detail); | |||
} | |||
} | |||
}; | |||
if (validateOnServer()) { | |||
VDragAndDropManager.get().visitServer( | |||
DragEventType.OVER, accpectedCb); | |||
} else { | |||
if (validates(currentDrag)) { | |||
accpectedCb.handleResponse(null); | |||
} else { | |||
keyToNode.get(currentMouseOverKey) | |||
.emphasis(null); | |||
} | |||
if (oldIdOver != null | |||
&& oldIdOver != currentMouseOverKey) { | |||
keyToNode.get(oldIdOver).emphasis(null); | |||
} | |||
updateTreeRelatedDragData(currentDrag); | |||
final String detail = getDropDetail(currentDrag | |||
.getCurrentGwtEvent()); | |||
boolean nodeHasChanged = (currentMouseOverKey != null && currentMouseOverKey != oldIdOver) | |||
|| (oldIdOver != null); | |||
boolean detailHasChanded = !detail.equals(oldDetail); | |||
if (nodeHasChanged || detailHasChanded) { | |||
ApplicationConnection.getConsole().log( | |||
"Change in Transferable " + currentMouseOverKey | |||
+ " " + detail); | |||
VAcceptCallback accpectedCb = new VAcceptCallback() { | |||
public void handleResponse(ValueMap responseData) { | |||
if (responseData == null // via client | |||
// side | |||
// validation | |||
|| responseData.containsKey("accepted")) { | |||
keyToNode.get(currentMouseOverKey) | |||
.emphasis(detail); | |||
} | |||
} | |||
}; | |||
if (validateOnServer()) { | |||
VDragAndDropManager.get().visitServer( | |||
DragEventType.OVER, accpectedCb); | |||
} else { | |||
if (validates(currentDrag)) { | |||
accpectedCb.handleResponse(null); | |||
} else { | |||
keyToNode.get(currentMouseOverKey).emphasis( | |||
null); | |||
} | |||
if (oldIdOver != null | |||
&& oldIdOver != currentMouseOverKey) { | |||
keyToNode.get(oldIdOver).emphasis(null); | |||
} | |||
} | |||
}); | |||
} | |||
} | |||
@Override | |||
@@ -385,9 +398,9 @@ public class VTree extends FlowPanel implements Paintable, VHasDropHandler { | |||
public void emphasis(String string) { | |||
// ApplicationConnection.getConsole().log("OUTLINE" + string); | |||
Style style = nodeCaptionDiv.getStyle(); | |||
String top = "Top".equals(string) ? "2px solid green" : null; | |||
String bottom = "Bottom".equals(string) ? "2px solid green" : null; | |||
String bg = "Center".equals(string) ? "green" : null; | |||
String top = "Top".equals(string) ? "2px solid green" : ""; | |||
String bottom = "Bottom".equals(string) ? "2px solid green" : ""; | |||
String bg = "Center".equals(string) ? "green" : ""; | |||
style.setProperty("borderTop", top); | |||
style.setProperty("borderBottom", bottom); | |||
@@ -439,12 +452,10 @@ public class VTree extends FlowPanel implements Paintable, VHasDropHandler { | |||
VTransferable t = new VTransferable(); | |||
t.setComponent(VTree.this); | |||
t.setItemId(key); | |||
VDragEvent drag = VDragAndDropManager.get().startDrag(t, | |||
mouseDownEvent, true); | |||
Element node = (Element) nodeCaptionDiv.cloneNode(true); | |||
node.getStyle().setOpacity(0.4); | |||
node.getStyle().setBackgroundColor("#999"); | |||
drag.setDragImage(node); | |||
VDragEvent drag = VDragAndDropManager.get().startDrag( | |||
t, mouseDownEvent, true); | |||
drag.createDragImage(nodeCaptionDiv, true); | |||
event.stopPropagation(); | |||
mouseDownEvent = null; | |||
@@ -453,6 +464,8 @@ public class VTree extends FlowPanel implements Paintable, VHasDropHandler { | |||
mouseDownEvent = null; | |||
} | |||
if (type == Event.ONMOUSEOVER) { | |||
ApplicationConnection.getConsole().log( | |||
"Treenode mouse over"); | |||
mouseDownEvent = null; | |||
currentMouseOverKey = key; | |||
event.stopPropagation(); |
@@ -3,6 +3,7 @@ package com.vaadin.terminal.gwt.client.ui.dd; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.dom.client.Style; | |||
import com.google.gwt.dom.client.Style.Display; | |||
import com.google.gwt.dom.client.Style.Position; | |||
import com.google.gwt.dom.client.Style.Unit; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
@@ -16,6 +17,7 @@ import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.MouseEventDetails; | |||
import com.vaadin.terminal.gwt.client.Paintable; | |||
import com.vaadin.terminal.gwt.client.Util; | |||
import com.vaadin.terminal.gwt.client.ValueMap; | |||
/** | |||
@@ -32,22 +34,90 @@ public class VDragAndDropManager { | |||
private final class DefaultDragAndDropEventHandler implements | |||
NativePreviewHandler { | |||
public void onPreviewNativeEvent(NativePreviewEvent event) { | |||
updateCurrentEvent(event.getNativeEvent()); | |||
NativeEvent nativeEvent = event.getNativeEvent(); | |||
updateCurrentEvent(nativeEvent); | |||
updateDragImagePosition(); | |||
NativeEvent nativeEvent = event.getNativeEvent(); | |||
int typeInt = event.getTypeInt(); | |||
Element targetElement = (Element) nativeEvent.getEventTarget() | |||
.cast(); | |||
if (dragElement != null && targetElement.isOrHasChild(dragElement)) { | |||
ApplicationConnection.getConsole().log( | |||
"Event on dragImage, ignored"); | |||
event.cancel(); | |||
nativeEvent.stopPropagation(); | |||
return; | |||
if (dragElement != null && dragElement.isOrHasChild(targetElement)) { | |||
// to detect the "real" target, hide dragelement temporary and | |||
// use elementFromPoint | |||
String display = dragElement.getStyle().getDisplay(); | |||
dragElement.getStyle().setDisplay(Display.NONE); | |||
try { | |||
int x = nativeEvent.getClientX(); | |||
int y = nativeEvent.getClientY(); | |||
// Util.browserDebugger(); | |||
targetElement = Util.getElementFromPoint(x, y); | |||
if (targetElement == null) { | |||
ApplicationConnection.getConsole().log( | |||
"Event on dragImage, ignored"); | |||
event.cancel(); | |||
nativeEvent.stopPropagation(); | |||
return; | |||
} else { | |||
ApplicationConnection.getConsole().log( | |||
"Event on dragImage, target changed"); | |||
// special handling for events over dragImage | |||
// pretty much all events are mousemove althout below | |||
// kind of happens mouseover | |||
switch (typeInt) { | |||
case Event.ONMOUSEOVER: | |||
case Event.ONMOUSEOUT: | |||
ApplicationConnection | |||
.getConsole() | |||
.log( | |||
"IGNORING proxy image event, fired because of hack or not significant"); | |||
// TODO consider if mouseover should actually do | |||
// same as mouse move. | |||
return; | |||
case Event.ONMOUSEMOVE: | |||
VDropHandler findDragTarget = findDragTarget(targetElement); | |||
if (findDragTarget != currentDropHandler) { | |||
// dragleave on old | |||
if (currentDropHandler != null) { | |||
currentDropHandler.dragLeave(currentDrag); | |||
acceptCallback = null; | |||
} | |||
// dragenter on new | |||
currentDropHandler = findDragTarget; | |||
if (currentDropHandler != null) { | |||
currentDrag | |||
.setElementOver((com.google.gwt.user.client.Element) targetElement); | |||
currentDropHandler.dragEnter(currentDrag); | |||
} | |||
} else if (findDragTarget != null) { | |||
currentDrag | |||
.setElementOver((com.google.gwt.user.client.Element) targetElement); | |||
currentDropHandler.dragOver(currentDrag); | |||
} | |||
nativeEvent.preventDefault(); // prevent text | |||
// selection on IE | |||
return; | |||
default: | |||
// just update element over and let the actual | |||
// handling code do the thing | |||
currentDrag | |||
.setElementOver((com.google.gwt.user.client.Element) targetElement); | |||
break; | |||
} | |||
} | |||
} catch (Exception e) { | |||
ApplicationConnection.getConsole().log( | |||
"FIXME : ERROR in elementFromPoint hack."); | |||
e.printStackTrace(); | |||
} finally { | |||
dragElement.getStyle().setProperty("display", display); | |||
} | |||
} | |||
int typeInt = event.getTypeInt(); | |||
switch (typeInt) { | |||
case Event.ONMOUSEOVER: | |||
ApplicationConnection.getConsole().log( | |||
@@ -99,6 +169,7 @@ public class VDragAndDropManager { | |||
} | |||
} | |||
} | |||
public enum DragEventType { | |||
@@ -258,8 +329,8 @@ public class VDragAndDropManager { | |||
private void updateDragImagePosition() { | |||
if (currentDrag.currentGwtEvent != null && dragElement != null) { | |||
Style style = dragElement.getStyle(); | |||
int clientY = currentDrag.currentGwtEvent.getClientY() + 6; | |||
int clientX = currentDrag.currentGwtEvent.getClientX() + 6; | |||
int clientY = currentDrag.currentGwtEvent.getClientY(); | |||
int clientX = currentDrag.currentGwtEvent.getClientX(); | |||
style.setTop(clientY, Unit.PX); | |||
style.setLeft(clientX, Unit.PX); | |||
} |
@@ -5,7 +5,9 @@ import java.util.HashMap; | |||
import java.util.Map; | |||
import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.dom.client.Style.Unit; | |||
import com.google.gwt.user.client.Element; | |||
import com.vaadin.terminal.gwt.client.BrowserInfo; | |||
/** | |||
* DragEvent used by Vaadin client side engine. Supports components, items, | |||
@@ -15,6 +17,8 @@ import com.google.gwt.user.client.Element; | |||
*/ | |||
public class VDragEvent { | |||
private static final int DEFAULT_OFFSET = 10; | |||
private static int eventId = 0; | |||
private VTransferable transferable; | |||
@@ -29,6 +33,8 @@ public class VDragEvent { | |||
private HashMap<String, Object> dropDetails = new HashMap<String, Object>(); | |||
private Element elementOver; | |||
VDragEvent(VTransferable t, NativeEvent startEvent) { | |||
transferable = t; | |||
this.startEvent = startEvent; | |||
@@ -52,6 +58,32 @@ public class VDragEvent { | |||
return new Date().getTime() - start.getTime(); | |||
} | |||
/** | |||
* Detecting the element on which the the event is happening may be | |||
* problematic during drag and drop operation. This is especially the case | |||
* if a drag image (often called drag proxy) is kept under the mouse cursor | |||
* (see {@link #createDragImage(Element, boolean)}. Drag and drop event | |||
* handlers (like the one provided by {@link VDragAndDropManager} ) should | |||
* set elmentOver field to reflect the the actual element on which the | |||
* pointer currently is (drag image excluded). {@link VDropHandler}s can | |||
* then more easily react properly on drag events by reading the element via | |||
* this method. | |||
* | |||
* @return the element in {@link VDropHandler} on which mouse cursor is on | |||
*/ | |||
public Element getElementOver() { | |||
if (elementOver != null) { | |||
return elementOver; | |||
} else if (currentGwtEvent != null) { | |||
return currentGwtEvent.getEventTarget().cast(); | |||
} | |||
return null; | |||
} | |||
public void setElementOver(Element targetElement) { | |||
elementOver = targetElement; | |||
} | |||
/** | |||
* Sets the element that will be used as "drag icon". | |||
* | |||
@@ -64,7 +96,7 @@ public class VDragEvent { | |||
* @param node | |||
*/ | |||
public void setDragImage(Element node) { | |||
VDragAndDropManager.get().setDragElement(node); | |||
setDragImage(node, DEFAULT_OFFSET, DEFAULT_OFFSET); | |||
} | |||
/** | |||
@@ -78,4 +110,39 @@ public class VDragEvent { | |||
return dropDetails; | |||
} | |||
public void setDragImage(Element cloneNode, int offsetX, int offsetY) { | |||
cloneNode.getStyle().setMarginLeft(offsetX, Unit.PX); | |||
cloneNode.getStyle().setMarginTop(offsetY, Unit.PX); | |||
VDragAndDropManager.get().setDragElement(cloneNode); | |||
} | |||
/** | |||
* Automatically tries to create a proxy image from given element. | |||
* | |||
* @param element | |||
* @param alignImageToEvent | |||
* if true, proxy image is aligned to start event, else next to | |||
* mouse cursor | |||
*/ | |||
public void createDragImage(Element element, boolean alignImageToEvent) { | |||
Element cloneNode = (Element) element.cloneNode(true); | |||
cloneNode.getStyle().setOpacity(0.4); | |||
if (BrowserInfo.get().isIE()) { | |||
cloneNode.getStyle().setProperty("filter", "alpha(opacity=70)"); | |||
} | |||
if (alignImageToEvent) { | |||
int absoluteTop = element.getAbsoluteTop(); | |||
int absoluteLeft = element.getAbsoluteLeft(); | |||
int clientX = startEvent.getClientX(); | |||
int clientY = startEvent.getClientY(); | |||
int offsetX = absoluteLeft - clientX; | |||
int offsetY = absoluteTop - clientY; | |||
setDragImage(cloneNode, offsetX, offsetY); | |||
} else { | |||
setDragImage(cloneNode); | |||
} | |||
} | |||
} |