here's a tutorial sample for creating selectable and draggable elements that can be dropped on each other (including lines). It consists of a custom marker class (for selectable and draggable icons), a custom line class (for selectable lines that can serve as drop targets) and a VectorLayer subclass to handle drag-and-drop events.
Code: Select all
<!DOCTYPE html>
<html>
<head>
<!--[if IE]>
<script type="text/javascript" src="webcomponent/script/excanvas.js"></script><![endif]-->
<script type="text/javascript" src="webcomponent/script/qooxdoo/script/qx-transport.js"></script>
<script type="text/javascript" src=".qxrpc"></script>
<script type="text/javascript" src="webcomponent/script/map.js"></script>
<script type="text/javascript">
// --> should be put into an external script
qxp.OO.defineClass("custom.IconElement",
com.ptvag.webcomponent.map.vector.AggregateElement,
function(x, y) {
com.ptvag.webcomponent.map.vector.AggregateElement.call(this);
var self = this;
var mImageMarker = null;
var mInfoBox = null;
// --> add other elements here if needed
var mShowingDropImage = false;
if (x != null) {
self.setX(x);
}
if (y != null) {
self.setY(y);
}
var createElements = function(vectorLayer) {
mImageMarker = new com.ptvag.webcomponent.map.vector.ImageMarker(
self.getX(), self.getY());
var VectorLayer = com.ptvag.webcomponent.map.layer.VectorLayer;
mImageMarker.setAlignment(VectorLayer.ALIGN_MID_HORIZ +
VectorLayer.ALIGN_MID_VERT);
self.addElement(mImageMarker);
// --> create other elements here if needed
setUrl();
};
self.dragOver = function(element) {
// return false if element can't be dropped here
mShowingDropImage = true;
setUrl();
return true;
};
self.dragOut = function(element) {
mShowingDropImage = false;
setUrl();
};
self.drop = function(element) {
alert("Dropped on element");
mShowingDropImage = false;
setUrl();
};
self.setOpacity = function(opacity) {
if (mImageMarker != null) {
mImageMarker.setOpacity(opacity);
}
if (opacity < 1) {
if (mInfoBox != null) {
self.removeElement(mInfoBox.getId());
mInfoBox = null;
}
} else if (self.getSelected() && mInfoBox == null) {
setUrl();
}
};
self._modifyX = function(propValue) {
if (mImageMarker != null) {
mImageMarker.setX(propValue);
}
if (mInfoBox != null) {
mInfoBox.setX(propValue);
}
// --> modify other elements here if needed
};
self._modifyY = function(propValue) {
if (mImageMarker != null) {
mImageMarker.setY(propValue);
}
if (mInfoBox != null) {
mInfoBox.setY(propValue);
}
// --> modify other elements here if needed
};
var setUrl = function() {
var mNeedInfoBox = false;
if (mImageMarker != null) {
if (mShowingDropImage) {
mImageMarker.setUrl(
"webcomponent/img/com/ptvag/webcomponent/map/button_reset.gif");
mNeedInfoBox = true;
} else if (self.getSelected()) {
mImageMarker.setUrl(
"webcomponent/img/com/ptvag/webcomponent/map/button_zoom_in.gif");
mNeedInfoBox = true;
} else {
mImageMarker.setUrl(
"webcomponent/img/com/ptvag/webcomponent/map/button_zoom_out.gif");
}
}
if (mNeedInfoBox && mInfoBox == null) {
mInfoBox = new com.ptvag.webcomponent.map.vector.InfoBox();
mInfoBox.setX(self.getX());
mInfoBox.setY(self.getY());
mInfoBox.setText("Element info");
self.addElement(mInfoBox);
} else if (!mNeedInfoBox && mInfoBox != null) {
self.removeElement(mInfoBox.getId());
mInfoBox = null;
}
};
self._modifySelected = function(propValue) {
setUrl();
};
self.getSquareDistance = function(evt) {
var distX = evt.relMouseX - mImageMarker.getRealX();
var distY = evt.relMouseY - mImageMarker.getRealY();
var squareDist = distX*distX + distY*distY;
if (squareDist <= 12*12) {
// --> modify as needed for sensitivity
return squareDist;
}
return -1;
}
// overridden
var superModifyVectorLayer = self._modifyVectorLayer;
self._modifyVectorLayer = function(propValue) {
superModifyVectorLayer.apply(self, arguments);
if (propValue != null) {
createElements(propValue);
}
};
});
qxp.OO.addProperty({ name:"x", type:qxp.constant.Type.NUMBER, allowNull:false });
qxp.OO.addProperty({ name:"y", type:qxp.constant.Type.NUMBER, allowNull:false });
qxp.OO.addProperty({ name:"selectable", type:qxp.constant.Type.BOOLEAN,
allowNull:false, defaultValue:false });
qxp.OO.addProperty({ name:"selected", type:qxp.constant.Type.BOOLEAN,
allowNull:false, defaultValue:false });
qxp.OO.addProperty({ name:"draggable", type:qxp.constant.Type.BOOLEAN,
allowNull:false, defaultValue:false });
// <--
// --> should be put into an external script
qxp.OO.defineClass("custom.LineElement",
com.ptvag.webcomponent.map.vector.AggregateElement,
function(lineCoords, infoX, infoY) {
com.ptvag.webcomponent.map.vector.AggregateElement.call(this);
var self = this;
var mLine = null;
var mInfoBox = null;
// --> add other elements here if needed
var mHighlighted = false;
var createElements = function(vectorLayer) {
mLine = new com.ptvag.webcomponent.map.vector.Line();
mLine.setCoordinates(lineCoords);
self.addElement(mLine);
// --> create other elements here if needed
updateLine();
};
self.dragOver = function(element) {
// return false if element can't be dropped here
mHighlighted = true;
updateLine();
return true;
};
self.dragOut = function(element) {
mHighlighted = false;
updateLine();
};
self.drop = function(element) {
alert("Dropped on line");
mHighlighted = false;
updateLine();
};
var updateLine = function() {
var mNeedInfoBox = false;
if (mHighlighted) {
mLine.setColor("rgba(10,10,255,0.8)");
mLine.setPixelSize(12);
mNeedInfoBox = true;
} else if (self.getSelected()) {
mLine.setColor("rgba(10,10,255,0.8)");
mLine.setPixelSize(10);
mNeedInfoBox = true;
} else {
mLine.setColor("rgba(10,10,255,0.5)");
mLine.setPixelSize(10);
}
if (mNeedInfoBox && mInfoBox == null) {
mInfoBox = new com.ptvag.webcomponent.map.vector.InfoBox();
mInfoBox.setX(infoX);
mInfoBox.setY(infoY);
mInfoBox.setText("Line info");
self.addElement(mInfoBox);
} else if (!mNeedInfoBox && mInfoBox != null) {
self.removeElement(mInfoBox.getId());
mInfoBox = null;
}
};
self._modifySelected = function(propValue) {
updateLine();
};
self.getSquareDistance = function(evt) {
return mLine.getSquareDistance(evt, 6).squareDistance;
// ==> adjust the tolerance (6) as needed
};
// overridden
var superModifyVectorLayer = self._modifyVectorLayer;
self._modifyVectorLayer = function(propValue) {
superModifyVectorLayer.apply(self, arguments);
if (propValue != null) {
createElements(propValue);
}
};
});
qxp.OO.addProperty({ name:"selectable", type:qxp.constant.Type.BOOLEAN,
allowNull:false, defaultValue:false });
qxp.OO.addProperty({ name:"selected", type:qxp.constant.Type.BOOLEAN,
allowNull:false, defaultValue:false });
// <--
// --> should be put into an external script
qxp.OO.defineClass("custom.VectorLayer",
com.ptvag.webcomponent.map.layer.VectorLayer,
function(floaterLayer, isSecondary) {
com.ptvag.webcomponent.map.layer.VectorLayer.call(this, floaterLayer,
(isSecondary == null ? true : isSecondary));
var self = this;
var mapPackage = com.ptvag.webcomponent.map;
var vectorPackage = mapPackage.vector;
var CoordUtil = mapPackage.CoordUtil;
var EventUtils = com.ptvag.webcomponent.util.EventUtils;
var significantMovementDist = 9;
var significantMovementSquareDist =
significantMovementDist*significantMovementDist;
var mCurrentElement = null;
var mSelectableElements = {};
var mDropTargets = {};
var mDragElementStartPoint;
var mMouseStartPoint;
var mSignificantMovement;
var mDropTarget = null;
var resetSelection = function(elementToIgnore) {
for (var id in mSelectableElements) {
var element = mSelectableElements[id];
if (element != elementToIgnore) {
element.setSelected(false);
}
}
}
self.getSelection = function() {
var selectedElements = [];
for (var id in mSelectableElements) {
var element = mSelectableElements[id];
if (element.getSelected()) {
selectedElements.push(element);
}
}
return selectedElements;
};
var superAddElement = self.addElement;
self.addElement = function(element) {
var id = superAddElement.apply(self, arguments);
if (element.getSelectable && element.getSelectable()) {
mSelectableElements[id] = element;
}
if (element.dragOver && element.dragOut && element.drop) {
mDropTargets[id] = element;
}
return id;
};
var superRemoveElement = self.removeElement;
self.removeElement = function(id) {
delete mSelectableElements[id];
delete mDropTargets[id];
superRemoveElement.apply(self, arguments);
};
var positionElement = function(element, position) {
var alreadyInBulkMode = self.inBulkMode();
if (!alreadyInBulkMode) {
self.startBulkMode();
}
element.setX(position.x);
element.setY(position.y);
element.setOpacity(mSignificantMovement ? 0.3 : 1);
if (!alreadyInBulkMode) {
self.endBulkMode();
}
};
var superOnMouseDown = self.onMouseDown;
self.onMouseDown = function(evt) {
if (mCurrentElement != null) {
// safeguard
if (mSignificantMovement) {
mSignificantMovement = false;
positionElement(mCurrentElement, mDragElementStartPoint);
if (mDropTarget != null) {
mDropTarget.dragOut(mCurrentElement);
mDropTarget = null;
}
}
mCurrentElement = null;
mDropTarget = null;
}
if (!com.ptvag.webcomponent.util.EventUtils.isLeftMouseButton(evt)) {
return superOnMouseDown.apply(self, arguments);
}
mMouseStartPoint = {relMouseX:evt.relMouseX, relMouseY:evt.relMouseY};
var startPoint = self.getMap().translateMouseCoords(mMouseStartPoint);
var modifiers = EventUtils.getModifiers(evt);
var minDist = -1;
for (var id in mSelectableElements) {
var element = mSelectableElements[id];
var squareDist = element.getSquareDistance(evt);
if (squareDist >= 0) {
if (minDist < 0 || squareDist < minDist) {
minDist = squareDist;
mCurrentElement = element;
}
}
}
if (mCurrentElement != null) {
if (modifiers & EventUtils.SHIFT_MASK) {
if (mCurrentElement.getSelected()) {
mCurrentElement.setSelected(false);
} else {
mCurrentElement.setSelected(true);
}
} else {
resetSelection(mCurrentElement);
mCurrentElement.setSelected(true);
}
if (self.hasEventListeners("selectionChanged")) {
self.createDispatchEvent("selectionChanged");
}
if (mCurrentElement.getDraggable && mCurrentElement.getDraggable()) {
mSignificantMovement = false;
mDragElementStartPoint = {x:mCurrentElement.getX(),
y:mCurrentElement.getY()};
}
self.getMap().getController().setActiveLayer(self);
return true;
}
return superOnMouseDown.apply(self, arguments);
};
var superOnMouseMove = self.onMouseMove;
self.onMouseMove = function(evt) {
if (mCurrentElement == null) {
return superOnMouseMove.apply(self, arguments);
}
var newPoint = self.getMap().translateMouseCoords(evt);
if (mCurrentElement != null &&
mCurrentElement.getDraggable && mCurrentElement.getDraggable()) {
if (!mSignificantMovement) {
var distX = evt.relMouseX - mMouseStartPoint.relMouseX;
var distY = evt.relMouseY - mMouseStartPoint.relMouseY;
var squareDist = distX*distX + distY*distY;
if (squareDist > significantMovementSquareDist) {
mSignificantMovement = true;
}
}
if (mSignificantMovement) {
positionElement(mCurrentElement, newPoint);
var minDist = -1;
var newDropTarget = null;
for (var id in mDropTargets) {
var element = mDropTargets[id];
if (element == mCurrentElement) {
continue;
}
var squareDist = element.getSquareDistance(evt);
if (squareDist >= 0) {
if (minDist < 0 || squareDist < minDist) {
minDist = squareDist;
newDropTarget = element;
}
}
}
if (newDropTarget != mDropTarget) {
if (mDropTarget != null) {
mDropTarget.dragOut(mCurrentElement);
mDropTarget = null;
}
if (newDropTarget != null) {
if (newDropTarget.dragOver(mCurrentElement)) {
mDropTarget = newDropTarget;
}
}
}
}
}
return false;
};
var superOnMouseUp = self.onMouseUp;
self.onMouseUp = function(evt) {
if (mCurrentElement == null) {
return superOnMouseUp.apply(self, arguments);
}
if (mCurrentElement != null) {
if (mSignificantMovement) {
mSignificantMovement = false;
positionElement(mCurrentElement, mDragElementStartPoint);
if (mDropTarget != null) {
mDropTarget.drop(mCurrentElement);
mDropTarget = null;
resetSelection();
}
}
mCurrentElement = null;
}
self.getMap().getController().setActiveLayer(null);
return true;
};
var superOnMouseClick = self.onMouseClick;
self.onMouseClick = function(evt) {
resetSelection();
if (self.hasEventListeners("selectionChanged")) {
self.createDispatchEvent("selectionChanged");
}
return superOnMouseClick.apply(self, arguments);
};
var superOnMouseOut = self.onMouseOut;
self.onMouseOut = function(evt) {
if (mCurrentElement == null) {
return superOnMouseOut.apply(self, arguments);
}
if (mCurrentElement != null) {
if (mSignificantMovement) {
mSignificantMovement = false;
positionElement(mCurrentElement, mDragElementStartPoint);
if (mDropTarget != null) {
mDropTarget.dragOut(mCurrentElement);
mDropTarget = null;
}
}
mCurrentElement = null;
}
self.getMap().getController().setActiveLayer(null);
return false;
};
var superOnKeyDown = self.onKeyDown;
self.onKeyDown = function(evt) {
if (evt.keyCode != EventUtils.KEY_CODE_ESC ||
mCurrentElement == null) {
return superOnKeyDown.apply(self, arguments);
}
self.onMouseOut();
self.getMap().getController().ignoreNextClick();
// otherwise, a click event would be generated by the following
// mouse up
return true;
};
});
// <--
function init() {
var container =
document.getElementById("mapContainer");
var map =
new com.ptvag.webcomponent.map.Map(container);
map.setCenter({x:4303250, y:5486500});
map.setZoom(10);
var floaterLayer = map.getLayer("floater");
var vectorLayer = new custom.VectorLayer(
floaterLayer);
vectorLayer.setIsRelative(true);
map.addLayer(vectorLayer, "customVectorLayer", null, floaterLayer);
var element1 = new custom.IconElement();
element1.setX(4303326);
element1.setY(5486754);
element1.setSelectable(true);
element1.setDraggable(true);
vectorLayer.addElement(element1);
var element2 = new custom.IconElement();
element2.setX(4303250);
element2.setY(5486100);
element2.setSelectable(true);
element2.setDraggable(true);
vectorLayer.addElement(element2);
var lineCoords = [
4303326,5487054,4303352,5487084,4303376,5487101,
4303392,5487110,4303397,5487112,4303416,5487128,
4303443,5487162,4303455,5487177,4303464,5487185,
4303522,5487230,4303526,5487233,4303530,5487237,
4303533,5487240,4303539,5487246,4303580,5487287,
4303595,5487300,4303633,5487334,4303710,5487383,
4303727,5487398,4303748,5487416,4303772,5487438,
4303776,5487440,4303790,5487449,4303820,5487460,
4303840,5487467,4303948,5487499,4303958,5487502,
4303968,5487505,4303990,5487513,4304017,5487529,
4304024,5487536,4304030,5487544,4304076,5487606,
4304090,5487623,4304098,5487632,4304133,5487656,
4304158,5487670,4304165,5487674,4304284,5487726,
4304319,5487741,4304411,5487797,4304443,5487813,
4304492,5487848,4304509,5487862,4304529,5487874
];
var line = new custom.LineElement(lineCoords, 4303776, 5487440);
line.setSelectable(true);
vectorLayer.addElement(line);
}
qxp.dev.log.Logger.ROOT_LOGGER.setMinLevel(qxp.dev.log.Logger.LEVEL_INFO);
qxp.dev.log.Logger.getClassLogger(qxp.core.Init).setMinLevel(qxp.dev.log.Logger.LEVEL_ERROR);
</script>
</head>
<body onload="init()">
<div id="mapContainer"
style="position:absolute;left:0;top:0;right:0;bottom:0">
</div>
</body>
</html>