[Libreoffice-commits] online.git: loleaflet/Makefile.am loleaflet/src
Dennis Francis (via logerrit)
logerrit at kemper.freedesktop.org
Wed Jul 8 14:58:06 UTC 2020
loleaflet/Makefile.am | 2
loleaflet/src/layer/vector/CircleMarker.js | 2
loleaflet/src/layer/vector/Path.Drag.js | 11
loleaflet/src/layer/vector/Path.Transform.SVG.js | 36 ++
loleaflet/src/layer/vector/Path.js | 137 +++++++++
loleaflet/src/layer/vector/Polygon.js | 4
loleaflet/src/layer/vector/Polyline.js | 2
loleaflet/src/layer/vector/Renderer.js | 67 ++++
loleaflet/src/layer/vector/SVG.js | 36 +-
loleaflet/src/layer/vector/SVGGroup.js | 224 ++++++++++++---
loleaflet/src/layer/vector/SplitPanesRenderer.js | 59 ++++
loleaflet/src/layer/vector/SplitPanesSVG.js | 323 +++++++++++++++++++++++
12 files changed, 815 insertions(+), 88 deletions(-)
New commits:
commit a59076e1ca88a6f2c8c67ce45bf769e963cf0717
Author: Dennis Francis <dennis.francis at collabora.com>
AuthorDate: Tue Jul 7 14:29:54 2020 +0530
Commit: Dennis Francis <dennis.francis at collabora.com>
CommitDate: Wed Jul 8 16:57:44 2020 +0200
add split-panes support for overlay layer
(This is only for svg renderer)
There are separate svg DOM-nodes for each split-pane with view-box set
appropriately. The L.Path based objects are shared for each
split-pane, but there will be separate identical 'path' DOM-nodes for
each svg container.
This patch introduces L.SplitPanesRenderer/L.SplitPanesSVG (has
same external api as L.Renderer/L.SVG). These are wrapper classes to host
child renderers, one per split-pane and delegate calls to them
appropriately.
Change-Id: Id44e9a1312500e6b43cdd8e4f42e235b43d22772
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/98354
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Tested-by: Jenkins
Reviewed-by: Dennis Francis <dennis.francis at collabora.com>
diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am
index 2b2dd22c8..589a95448 100644
--- a/loleaflet/Makefile.am
+++ b/loleaflet/Makefile.am
@@ -234,6 +234,8 @@ LOLEAFLET_JS =\
src/layer/vector/CircleMarker.js \
src/layer/vector/Circle.js \
src/layer/vector/SVG.js \
+ src/layer/vector/SplitPanesRenderer.js \
+ src/layer/vector/SplitPanesSVG.js \
src/layer/vector/Path.Transform.SVG.js \
src/core/Handler.js \
src/layer/vector/SVGGroup.js \
diff --git a/loleaflet/src/layer/vector/CircleMarker.js b/loleaflet/src/layer/vector/CircleMarker.js
index 837471440..0e45bddf6 100644
--- a/loleaflet/src/layer/vector/CircleMarker.js
+++ b/loleaflet/src/layer/vector/CircleMarker.js
@@ -66,7 +66,7 @@ L.CircleMarker = L.Path.extend({
},
_empty: function () {
- return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
+ return this._radius && !this._renderer.intersectsBounds(this._pxBounds);
}
});
diff --git a/loleaflet/src/layer/vector/Path.Drag.js b/loleaflet/src/layer/vector/Path.Drag.js
index 4ca7ff847..b1375aacf 100644
--- a/loleaflet/src/layer/vector/Path.Drag.js
+++ b/loleaflet/src/layer/vector/Path.Drag.js
@@ -72,9 +72,7 @@ L.Handler.PathDrag = L.Handler.extend(/** @lends L.Path.Drag.prototype */ {
(this._path.options.className + ' ' + L.Handler.PathDrag.DRAGGING_CLS) :
L.Handler.PathDrag.DRAGGING_CLS;
- if (this._path._path) {
- L.DomUtil.addClass(this._path._path, L.Handler.PathDrag.DRAGGING_CLS);
- }
+ this._path.addClass(L.Handler.PathDrag.DRAGGING_CLS);
},
/**
@@ -85,9 +83,8 @@ L.Handler.PathDrag = L.Handler.extend(/** @lends L.Path.Drag.prototype */ {
this._path.options.className = this._path.options.className
.replace(new RegExp('\\s+' + L.Handler.PathDrag.DRAGGING_CLS), '');
- if (this._path._path) {
- L.DomUtil.removeClass(this._path._path, L.Handler.PathDrag.DRAGGING_CLS);
- }
+
+ this._path.removeClass(L.Handler.PathDrag.DRAGGING_CLS);
if (!this._path.options.manualDrag) {
L.DomEvent.off(document, 'mousemove touchmove', this._onDrag, this);
@@ -119,7 +116,7 @@ L.Handler.PathDrag = L.Handler.extend(/** @lends L.Path.Drag.prototype */ {
this._matrix = [1, 0, 0, 1, 0, 0];
L.DomEvent.stop(evt.originalEvent);
- L.DomUtil.addClass(this._path._renderer._container, 'leaflet-interactive');
+ this._path._renderer.addContainerClass('leaflet-interactive');
if (!this._path.options.manualDrag) {
L.DomEvent
diff --git a/loleaflet/src/layer/vector/Path.Transform.SVG.js b/loleaflet/src/layer/vector/Path.Transform.SVG.js
index fb76c9cf6..4abc4528a 100644
--- a/loleaflet/src/layer/vector/Path.Transform.SVG.js
+++ b/loleaflet/src/layer/vector/Path.Transform.SVG.js
@@ -4,7 +4,7 @@ L.SVG.include({
* Reset transform matrix
*/
_resetTransformPath: function(layer) {
- layer._path.setAttributeNS(null, 'transform', '');
+ layer.getPathNode(this).setAttributeNS(null, 'transform', '');
},
/**
@@ -13,8 +13,40 @@ L.SVG.include({
* @param {Array.<Number>} matrix
*/
transformPath: function(layer, matrix) {
- layer._path.setAttributeNS(null, 'transform',
+ layer.getPathNode(this).setAttributeNS(null, 'transform',
'matrix(' + matrix.join(' ') + ')');
}
});
+
+L.SplitPanesSVG.include({
+ /**
+ * Reset transform matrix
+ */
+ _resetTransformPath: function(layer) {
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._resetTransformPath(layer);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._resetTransformPath(layer);
+ });
+ },
+
+ /**
+ * Applies matrix transformation to SVG
+ * @param {L.Path} layer
+ * @param {Array.<Number>} matrix
+ */
+ transformPath: function(layer, matrix) {
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed'].transformPath(layer, matrix);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer.transformPath(layer, matrix);
+ });
+ }
+});
diff --git a/loleaflet/src/layer/vector/Path.js b/loleaflet/src/layer/vector/Path.js
index 51665ba9d..1c06e53ef 100644
--- a/loleaflet/src/layer/vector/Path.js
+++ b/loleaflet/src/layer/vector/Path.js
@@ -21,10 +21,12 @@ L.Path = L.Layer.extend({
fillRule: 'evenodd',
// className: ''
- interactive: true
+ interactive: true,
+ fixed: false,
},
onAdd: function () {
+ this._pathNodeCollection = new L.Path.PathNodeCollection();
this._renderer = this._map.getRenderer(this);
this._renderer._initPath(this);
this._reset();
@@ -33,6 +35,7 @@ L.Path = L.Layer.extend({
onRemove: function () {
this._renderer._removePath(this);
+ this._pathNodeCollection.clear();
},
getEvents: function () {
@@ -80,5 +83,135 @@ L.Path = L.Layer.extend({
_clickTolerance: function () {
// used when doing hit detection for Canvas layers
return (this.options.stroke ? this.options.weight / 2 : 0) + (L.Browser.touch ? 10 : 0);
- }
+ },
+
+ addPathNode: function (pathNode, actualRenderer) {
+
+ this._path = undefined;
+
+ if (!this._pathNodeCollection) {
+ this._pathNodeCollection = new L.Path.PathNodeCollection();
+ }
+
+ this._pathNodeCollection.add(new L.Path.PathNodeData(pathNode, actualRenderer));
+ },
+
+ getPathNode: function (actualRenderer) {
+ return this._pathNodeCollection.getPathNode(actualRenderer);
+ },
+
+ addClass: function (className) {
+ this._pathNodeCollection.addOrRemoveClass(className, true /* add */);
+ },
+
+ removeClass: function (className) {
+ this._pathNodeCollection.addOrRemoveClass(className, false /* add */);
+ },
+
+});
+
+L.Path.PathNodeData = L.Class.extend({
+
+ initialize: function (pathNode, actualRenderer) {
+
+ console.assert(pathNode, 'invalid pathNode argument!');
+ console.assert(actualRenderer, 'invalid actualRenderer argument!');
+
+ if (!(pathNode instanceof Node)) {
+ console.error('Not a node instance!');
+ }
+
+ this._pathNode = pathNode;
+ this._actualRenderer = actualRenderer;
+ this._data = {};
+ },
+
+ key: function () {
+ return L.Path.PathNodeData.key(this._actualRenderer);
+ },
+
+ getNode: function () {
+ if (!(this._pathNode instanceof Node)) {
+ console.error('Not a node instance!');
+ }
+ return this._pathNode;
+ },
+
+ getActualRenderer: function () {
+ return this._actualRenderer;
+ },
+
+ setCustomField: function (fieldName, value) {
+ console.assert(typeof fieldName === 'string' && fieldName, 'invalid fieldName');
+ this._data[fieldName] = value;
+ },
+
+ getCustomField: function (fieldName) {
+ console.assert(typeof fieldName === 'string' && fieldName, 'invalid fieldName');
+ return this._data[fieldName];
+ },
+
+ clearCustomField: function (fieldName) {
+ console.assert(typeof fieldName === 'string' && fieldName, 'invalid fieldName');
+ delete this._data[fieldName];
+ },
+
+ addOrRemoveClass: function (className, add) {
+ if (add) {
+ L.DomUtil.addClass(this._pathNode, className);
+ }
+ else {
+ L.DomUtil.removeClass(this._pathNode, className);
+ }
+ },
+
+});
+
+L.Path.PathNodeData.key = function (layer) {
+ return L.stamp(layer);
+};
+
+L.Path.PathNodeCollection = L.Class.extend({
+
+ initialize: function () {
+ this.clear();
+ },
+
+ add: function (pathNodeData) {
+
+ console.assert(pathNodeData instanceof L.Path.PathNodeData,
+ 'invalid pathNodeData argument!');
+
+ this._collection[pathNodeData.key()] = pathNodeData;
+ },
+
+ clear: function () {
+ this._collection = {};
+ },
+
+ getPathNode: function (actualRenderer) {
+
+ console.assert(actualRenderer, 'invalid actualRenderer argument!');
+ var key = L.Path.PathNodeData.key(actualRenderer);
+ var nodeData = this._collection[key];
+
+ console.assert(nodeData, 'cannot find path node!');
+
+ return nodeData.getNode();
+ },
+
+ forEachNode: function (callback) {
+ var that = this;
+ Object.keys(this._collection).forEach(function (key) {
+ callback(that._collection[key]);
+ });
+ },
+
+ addOrRemoveClass: function (className, add) {
+ console.assert(className, 'className not provided!');
+ this.forEachNode(function (nodeData) {
+ nodeData.addOrRemoveClass(className, add);
+ });
+ },
+
});
diff --git a/loleaflet/src/layer/vector/Polygon.js b/loleaflet/src/layer/vector/Polygon.js
index 647438a8b..25118e62c 100644
--- a/loleaflet/src/layer/vector/Polygon.js
+++ b/loleaflet/src/layer/vector/Polygon.js
@@ -42,7 +42,9 @@ L.Polygon = L.Polyline.extend({
},
_clipPoints: function () {
- if (this.options.noClip) {
+ if (this.options.noClip || this._renderer instanceof L.SplitPanesSVG) {
+ // TODO: need some work to get this right and performant, especially in the case of
+ // a poly* spread across multiple split-panes.
this._parts = this._rings;
return;
}
diff --git a/loleaflet/src/layer/vector/Polyline.js b/loleaflet/src/layer/vector/Polyline.js
index c0650db51..0cab5e463 100644
--- a/loleaflet/src/layer/vector/Polyline.js
+++ b/loleaflet/src/layer/vector/Polyline.js
@@ -165,7 +165,7 @@ L.Polyline = L.Path.extend({
// clip polyline by renderer bounds so that we have less to render for performance
_clipPoints: function () {
- if (this.options.noClip) {
+ if (this.options.noClip || this._renderer instanceof L.SplitPanesSVG) {
this._parts = this._rings;
return;
}
diff --git a/loleaflet/src/layer/vector/Renderer.js b/loleaflet/src/layer/vector/Renderer.js
index eff612bc9..b2507db6d 100644
--- a/loleaflet/src/layer/vector/Renderer.js
+++ b/loleaflet/src/layer/vector/Renderer.js
@@ -17,6 +17,11 @@ L.Renderer = L.Layer.extend({
L.stamp(this);
},
+ setParentRenderer: function (parent) {
+ console.assert(parent !== this, 'self reference');
+ this._parentRenderer = parent;
+ },
+
onAdd: function () {
if (!this._container) {
this._initContainer(); // defined by renderer implementations
@@ -26,7 +31,13 @@ L.Renderer = L.Layer.extend({
}
}
- this.getPane().appendChild(this._container);
+ if (this._parentRenderer) {
+ this._parentRenderer.getContainer().appendChild(this._container);
+ }
+ else {
+ this.getPane().appendChild(this._container);
+ }
+
this._update();
},
@@ -53,12 +64,41 @@ L.Renderer = L.Layer.extend({
_update: function () {
// update pixel bounds of renderer container (for positioning/sizing/clipping later)
+ if (this._parentRenderer) {
+ var posBounds = this._parentRenderer.getChildPosBounds(this);
+ this._position = posBounds.position;
+ this._bounds = posBounds.bounds;
+ return;
+ }
+
var p = this.options.padding,
size = this._map.getSize(),
- min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
+ min = this._map.containerPointToLayerPointIgnoreSplits(size.multiplyBy(-p)).round();
this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
- }
+ this._position = this._bounds.min;
+ },
+
+ getContainer: function () {
+ return this._container;
+ },
+
+ getBounds: function () {
+ return this._bounds;
+ },
+
+ intersectsBounds: function (pxBounds) {
+ return this._bounds.intersects(pxBounds);
+ },
+
+ addContainerClass: function (className) {
+ L.DomUtil.addClass(this._container, className);
+ },
+
+ removeContainerClass: function (className) {
+ L.DomUtil.removeClass(this._container, className);
+ },
+
});
@@ -68,9 +108,17 @@ L.Map.include({
var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
if (!renderer) {
- renderer = this._renderer = (L.SVG && L.svg()) || (L.Canvas && L.canvas());
+ if (this._splitPanesContext) {
+ renderer = this._renderer = (L.SVG && L.SplitPanesSVG && L.splitPanesSVG()) ||
+ (L.Canvas && L.SplitPanesCanvas && L.splitPanesCanvas());
+ }
+ else {
+ renderer = this._renderer = (L.SVG && L.svg()) || (L.Canvas && L.canvas());
+ }
}
+ console.assert(renderer, 'Could create a renderer!');
+
if (!this.hasLayer(renderer)) {
this.addLayer(renderer);
}
@@ -84,9 +132,18 @@ L.Map.include({
var renderer = this._paneRenderers[name];
if (renderer === undefined) {
- renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
+ if (this._splitPanesContext) {
+ renderer = (L.SVG && L.SplitPanesSVG && L.splitPanesSVG({pane: name})) ||
+ (L.Canvas && L.SplitPanesCanvas && L.splitPanesCanvas({pane: name}));
+ }
+ else {
+ renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
+ }
+
+ console.assert(renderer, 'Could create a renderer!');
this._paneRenderers[name] = renderer;
}
+
return renderer;
}
});
diff --git a/loleaflet/src/layer/vector/SVG.js b/loleaflet/src/layer/vector/SVG.js
index 4ae1e3edb..3ac66e901 100644
--- a/loleaflet/src/layer/vector/SVG.js
+++ b/loleaflet/src/layer/vector/SVG.js
@@ -17,11 +17,12 @@ L.SVG = L.Renderer.extend({
L.Renderer.prototype._update.call(this);
- var b = this._bounds,
- size = b.getSize(),
- container = this._container;
+ var b = this._bounds;
+ var size = b.getSize();
+ var position = this._position;
+ var container = this._container;
- L.DomUtil.setPosition(container, b.min);
+ L.DomUtil.setPosition(container, position);
// set size of svg-container if changed
if (!this._svgSize || !this._svgSize.equals(size)) {
@@ -31,14 +32,15 @@ L.SVG = L.Renderer.extend({
}
// movement: update container viewBox so that we don't have to change coordinates of individual layers
- L.DomUtil.setPosition(container, b.min);
+ L.DomUtil.setPosition(container, position);
container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
},
// methods below are called by vector layers implementations
_initPath: function (layer) {
- var path = layer._path = L.SVG.create('path');
+ var path = L.SVG.create('path');
+ layer.addPathNode(path, this);
if (layer.options.className) {
L.DomUtil.addClass(path, layer.options.className);
@@ -57,7 +59,7 @@ L.SVG = L.Renderer.extend({
},
_initGroup: function (layer) {
- layer._path = L.SVG.create('g');
+ layer.addPathNode(L.SVG.create('g'), this);
},
_fireMouseEvent: function (e) {
@@ -77,21 +79,21 @@ L.SVG = L.Renderer.extend({
},
_addGroup: function (layer) {
- this._container.appendChild(layer._path);
+ this._container.appendChild(layer.getPathNode(this));
},
_addPath: function (layer) {
- this._container.appendChild(layer._path);
- layer.addInteractiveTarget(layer._path);
+ this._container.appendChild(layer.getPathNode(this));
+ layer.addInteractiveTarget(layer.getPathNode(this));
},
_removeGroup: function (layer) {
- L.DomUtil.remove(layer._path);
+ L.DomUtil.remove(layer.getPathNode(this));
},
_removePath: function (layer) {
- L.DomUtil.remove(layer._path);
- layer.removeInteractiveTarget(layer._path);
+ L.DomUtil.remove(layer.getPathNode(this));
+ layer.removeInteractiveTarget(layer.getPathNode(this));
},
_updatePath: function (layer) {
@@ -100,7 +102,7 @@ L.SVG = L.Renderer.extend({
},
_updateStyle: function (layer) {
- var path = layer._path,
+ var path = layer.getPathNode(this),
options = layer.options;
if (!path) { return; }
@@ -158,16 +160,16 @@ L.SVG = L.Renderer.extend({
},
_setPath: function (layer, path) {
- layer._path.setAttribute('d', path);
+ layer.getPathNode(this).setAttribute('d', path);
},
// SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
_bringToFront: function (layer) {
- L.DomUtil.toFront(layer._path);
+ L.DomUtil.toFront(layer.getPathNode(this));
},
_bringToBack: function (layer) {
- L.DomUtil.toBack(layer._path);
+ L.DomUtil.toBack(layer.getPathNode(this));
}
});
diff --git a/loleaflet/src/layer/vector/SVGGroup.js b/loleaflet/src/layer/vector/SVGGroup.js
index 8ded23f26..7aca7c219 100644
--- a/loleaflet/src/layer/vector/SVGGroup.js
+++ b/loleaflet/src/layer/vector/SVGGroup.js
@@ -12,8 +12,10 @@ L.SVGGroup = L.Layer.extend({
initialize: function (bounds, options) {
L.setOptions(this, options);
+ this._pathNodeCollection = new L.Path.PathNodeCollection();
this._bounds = bounds;
this._rect = L.rectangle(bounds, this.options);
+ this._hasSVGNode = false;
if (L.Browser.touch && !L.Browser.pointer) {
this.options.manualDrag = true;
}
@@ -23,20 +25,28 @@ L.SVGGroup = L.Layer.extend({
},
setVisible: function (visible) {
- if (this._svg != null) {
+ this._forEachSVGNode(function (svgNode) {
+ svgNode.setAttribute('opacity', 0);
if (visible)
- this._svg.setAttribute('visibility', 'visible');
+ svgNode.setAttribute('visibility', 'visible');
else
- this._svg.setAttribute('visibility', 'hidden');
- }
+ svgNode.setAttribute('visibility', 'hidden');
+ });
},
sizeSVG: function () {
+
+ if (!this._hasSVGNode) {
+ return;
+ }
+
var size = L.bounds(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
this._map.latLngToLayerPoint(this._bounds.getSouthEast())).getSize();
- this._svg.setAttribute('width', size.x);
- this._svg.setAttribute('height', size.y);
+ this._forEachSVGNode(function (svgNode) {
+ svgNode.setAttribute('width', size.x);
+ svgNode.setAttribute('height', size.y);
+ });
},
parseSVG: function (svgString) {
@@ -45,30 +55,41 @@ L.SVGGroup = L.Layer.extend({
},
addEmbeddedSVG: function (svgString) {
- var doc = this.parseSVG(svgString);
+ var svgDoc = this.parseSVG(svgString);
- if (doc.lastChild.localName !== 'svg')
+ if (svgDoc.lastChild.localName !== 'svg')
return;
- this._svg = this._path.insertBefore(doc.lastChild, this._rect._path);
- this._dragShape = this._rect._path;
- this._svg.setAttribute('pointer-events', 'none');
- this._svg.setAttribute('opacity', this._dragStarted ? 1 : 0);
+ var svgLastChild = svgDoc.lastChild;
+ var thisObj = this;
+ this._forEachGroupNode(function (groupNode, rectNode, nodeData) {
+ var svgNode = groupNode.insertBefore(svgLastChild, rectNode);
+ nodeData.setCustomField('svg', svgNode);
+ nodeData.setCustomField('dragShape', rectNode);
+ thisObj._dragShapePresent = true;
+ svgNode.setAttribute('pointer-events', 'none');
+ svgNode.setAttribute('opacity', thisObj._dragStarted ? 1 : 0);
+ });
+
+ this._hasSVGNode = true;
+
this.sizeSVG();
this._update();
},
_onDragStart: function(evt) {
- if (!this._map || !this._dragShape || !this.dragging)
+ if (!this._map || !this._dragShapePresent || !this.dragging)
return;
this._dragStarted = true;
this._moved = false;
if (!this.options.manualDrag) {
- L.DomEvent.on(this._dragShape, 'mousemove', this._onDrag, this);
- L.DomEvent.on(this._dragShape, 'mouseup', this._onDragEnd, this);
- if (this.dragging.constraint)
- L.DomEvent.on(this._dragShape, 'mouseout', this._onDragEnd, this);
+ this._forEachDragShape(function (dragShape) {
+ L.DomEvent.on(dragShape, 'mousemove', this._onDrag, this);
+ L.DomEvent.on(dragShape, 'mouseup', this._onDragEnd, this);
+ if (this.dragging.constraint)
+ L.DomEvent.on(dragShape, 'mouseout', this._onDragEnd, this);
+ }.bind(this));
}
var data = {
@@ -82,7 +103,7 @@ L.SVGGroup = L.Layer.extend({
},
_onDrag: function(evt) {
- if (!this._map || !this._dragShape || !this.dragging)
+ if (!this._map || !this._dragShapePresent || !this.dragging)
return;
if (!this._moved) {
@@ -94,14 +115,16 @@ L.SVGGroup = L.Layer.extend({
},
_onDragEnd: function(evt) {
- if (!this._map || !this._dragShape || !this.dragging)
+ if (!this._map || !this._dragShapePresent || !this.dragging)
return;
if (!this.options.manualDrag) {
- L.DomEvent.off(this._dragShape, 'mousemove', this._onDrag, this);
- L.DomEvent.off(this._dragShape, 'mouseup', this._onDragEnd, this);
- if (this.dragging.constraint)
- L.DomEvent.off(this._dragShape, 'mouseout', this._onDragEnd, this);
+ this._forEachDragShape(function (dragShape) {
+ L.DomEvent.off(dragShape, 'mousemove', this._onDrag, this);
+ L.DomEvent.off(dragShape, 'mouseup', this._onDragEnd, this);
+ if (this.dragging.constraint)
+ L.DomEvent.off(dragShape, 'mouseout', this._onDragEnd, this);
+ }.bind(this));
}
this._moved = false;
@@ -146,53 +169,78 @@ L.SVGGroup = L.Layer.extend({
this._renderer._initPath(this._rect);
this._renderer._addGroup(this);
- if (this._path && this._rect._path) {
+ this._forEachGroupNode(function (groupNode, rectNode, nodeData) {
+
+ if (!groupNode || !rectNode) {
+ return;
+ }
+
this._rect._map = this._map;
this._rect._renderer = this._renderer;
- L.DomUtil.addClass(this._path, 'leaflet-control-buttons-disabled');
+ L.DomUtil.addClass(groupNode, 'leaflet-control-buttons-disabled');
if (this.options.svg) {
var doc = this.parseSVG(this.options.svg);
if (doc && doc.lastChild.localName === 'svg') {
- this._svg = this._path.appendChild(doc.lastChild);
- this._svg.setAttribute('opacity', 0);
- this._svg.setAttribute('pointer-events', 'none');
- this.sizeSVG();
+ this._hasSVGNode = true;
+ var svgNode = groupNode.appendChild(doc.lastChild);
+ nodeData.setCustomField('svg', svgNode);
+ svgNode.setAttribute('opacity', 0);
+ svgNode.setAttribute('pointer-events', 'none');
}
delete this.options.svg;
}
- this._path.appendChild(this._rect._path);
- this._dragShape = this._rect._path;
+ groupNode.appendChild(rectNode);
+ nodeData.setCustomField('dragShape', rectNode);
+ this._dragShapePresent = true;
if (!this.options.manualDrag) {
- L.DomEvent.on(this._rect._path, 'mousedown', this._onDragStart, this);
+ L.DomEvent.on(rectNode, 'mousedown', this._onDragStart, this);
}
- }
+ }.bind(this));
+
+ this.sizeSVG();
+
this._update();
},
onRemove: function () {
this._rect._map = this._rect._renderer = null;
- this.removeInteractiveTarget(this._rect._path);
- L.DomUtil.remove(this._rect._path);
+ this._pathNodeCollection.forEachNode(function (nodeData) {
+
+ var actualRenderer = nodeData.getActualRenderer();
+ var rectNode = this._rect.getPathNode(actualRenderer);
+
+ this.removeInteractiveTarget(rectNode);
+ L.DomUtil.remove(rectNode);
+
+ }.bind(this));
+
this.removeEmbeddedSVG();
this._renderer._removeGroup(this);
},
removeEmbeddedSVG: function () {
- if (this._svg) {
- this._dragShape = null;
- L.DomUtil.remove(this._svg);
- delete this._svg;
- this._update();
+ if (!this._hasSVGNode) {
+ return;
}
+
+ this._pathNodeCollection.forEachNode(function (nodeData) {
+ var svgNode = nodeData.getCustomField('svg');
+ L.DomUtil.remove(svgNode);
+ nodeData.clearCustomField('svg');
+ });
+
+ this._dragShapePresent = false;
+ this._hasSVGNode = false;
+ this._update();
},
_hideEmbeddedSVG: function () {
- if (this._svg) {
- this._svg.setAttribute('opacity', 0);
- }
+ this._forEachSVGNode(function (svgNode) {
+ svgNode.setAttribute('opacity', 0);
+ });
},
_transform: function(matrix) {
@@ -216,23 +264,95 @@ L.SVGGroup = L.Layer.extend({
},
_showEmbeddedSVG: function () {
- if (this._svg) {
- this._svg.setAttribute('opacity', 1);
- }
+ this._forEachSVGNode(function (svgNode) {
+ svgNode.setAttribute('opacity', 1);
+ });
},
_update: function () {
this._rect.setBounds(this._bounds);
- if (this._svg) {
- var point = this._map.latLngToLayerPoint(this._bounds.getNorthWest());
- this._svg.setAttribute('x', point.x);
- this._svg.setAttribute('y', point.y);
- }
+ var point = this._map.latLngToLayerPoint(this._bounds.getNorthWest());
+
+ this._forEachSVGNode(function (svgNode) {
+ svgNode.setAttribute('x', point.x);
+ svgNode.setAttribute('y', point.y);
+ }.bind(this));
},
_updatePath: function () {
this._update();
- }
+ },
+
+ addPathNode: function (pathNode, actualRenderer) {
+
+ this._path = undefined;
+
+ if (!this._pathNodeCollection) {
+ this._pathNodeCollection = new L.Path.PathNodeCollection();
+ }
+
+ this._pathNodeCollection.add(new L.Path.PathNodeData(pathNode, actualRenderer));
+ },
+
+ getPathNode: function (actualRenderer) {
+
+ console.assert(this._pathNodeCollection, 'missing _pathNodeCollection member!');
+ return this._pathNodeCollection.getPathNode(actualRenderer);
+ },
+
+ addClass: function (className) {
+ this._pathNodeCollection.addOrRemoveClass(className, true /* add */);
+ },
+
+ removeClass: function (className) {
+ this._pathNodeCollection.addOrRemoveClass(className, false /* add */);
+ },
+
+ _forEachGroupNode: function (callback) {
+
+ var that = this;
+ this._pathNodeCollection.forEachNode(function (nodeData) {
+
+ var actualRenderer = nodeData.getActualRenderer();
+ var groupNode = nodeData.getNode();
+ var rectNode = that._rect.getPathNode(actualRenderer);
+
+ callback(groupNode, rectNode, nodeData);
+
+ });
+
+ return true;
+ },
+
+ _forEachSVGNode: function (callback) {
+ if (!this._hasSVGNode) {
+ return false;
+ }
+
+ this._pathNodeCollection.forEachNode(function (nodeData) {
+ var svgNode = nodeData.getCustomField('svg');
+ if (svgNode) {
+ callback(svgNode);
+ }
+ });
+
+ return true;
+ },
+
+ _forEachDragShape: function (callback) {
+ if (!this._dragShapePresent) {
+ return false;
+ }
+
+ this._pathNodeCollection.forEachNode(function (nodeData) {
+ var dragShape = nodeData.getCustomField('dragShape');
+ if (dragShape) {
+ callback(dragShape);
+ }
+ });
+
+ return true;
+ },
});
diff --git a/loleaflet/src/layer/vector/SplitPanesRenderer.js b/loleaflet/src/layer/vector/SplitPanesRenderer.js
new file mode 100644
index 000000000..4f7f06073
--- /dev/null
+++ b/loleaflet/src/layer/vector/SplitPanesRenderer.js
@@ -0,0 +1,59 @@
+/* -*- js-indent-level: 8 -*- */
+/*
+ * L.SplitPanesRenderer is a base class for split-panes renderer implementations (only SVG for now);
+ * handles renderer container, bounds and zoom animation.
+ */
+
+L.SplitPanesRenderer = L.Layer.extend({
+
+ options: {
+ // how much to extend the clip area around the map view (relative to its size)
+ // e.g. 0.1 would be 10% of map view in each direction; defaults to clip with the map view
+ padding: 0
+ },
+
+ initialize: function (options) {
+ L.setOptions(this, options);
+ L.stamp(this);
+ },
+
+ onAdd: function () {
+
+ this._splitPanesContext = this._map.getSplitPanesContext();
+ console.assert(this._splitPanesContext, 'no split-panes context object!');
+
+ if (!this._container) {
+ this._initContainer(); // defined by renderer implementations
+ }
+
+ this.getPane().appendChild(this._container);
+ this._update();
+ },
+
+ onRemove: function () {
+ L.DomUtil.remove(this._container);
+ },
+
+ setParentRenderer: function () {
+ console.error('SplitPanesRenderer cannot be a child renderer!');
+ },
+
+ // All child renderers have dedicated event listeners.
+ getEvents: function () {
+ return {};
+ },
+
+ _animateZoom: function () {
+ },
+
+ _update: function () {
+ },
+
+ getContainer: function () {
+ return this._container;
+ },
+
+ getBoundsList: function () {
+ return undefined;
+ },
+});
diff --git a/loleaflet/src/layer/vector/SplitPanesSVG.js b/loleaflet/src/layer/vector/SplitPanesSVG.js
new file mode 100644
index 000000000..c0f8974ce
--- /dev/null
+++ b/loleaflet/src/layer/vector/SplitPanesSVG.js
@@ -0,0 +1,323 @@
+/* -*- js-indent-level: 8 -*- */
+/*
+ * L.SplitPanesSVG renders vector layers with SVG for split-panes.
+ */
+
+L.SplitPanesSVG = L.SplitPanesRenderer.extend({
+ _initContainer: function () {
+
+ this._container = document.createElement('div');
+
+ this._setupPaneRenderers();
+ },
+
+ _setupPaneRenderers: function () {
+
+ if (this._childRenderers) {
+ return;
+ }
+
+ var map = this._map;
+ this._rendererIds = ['fixed', 'topleft', 'topright', 'bottomleft', 'bottomright'];
+ this._splitPaneNames = ['topleft', 'topright', 'bottomleft', 'bottomright'];
+ this._childRenderers = {};
+ this._rendererIds.forEach(function (rendererId) {
+ var svgRenderer = L.svg(this.options);
+ this._childRenderers[rendererId] = svgRenderer;
+ svgRenderer.rendererId = rendererId;
+ svgRenderer.setParentRenderer(this);
+ map.addLayer(svgRenderer);
+ }, this);
+ },
+
+ _forEachPaneRenderer: function (callback) {
+ return this._forEachChildRenderer(callback, true /* skipFixed */);
+ },
+
+ _forEachChildRenderer: function (callback, skipFixed) {
+ if (!this._childRenderers) {
+ return false;
+ }
+
+ this._rendererIds.forEach(function (rendererId) {
+
+ if (skipFixed === true && rendererId === 'fixed') {
+ return;
+ }
+
+ var renderer = this._childRenderers[rendererId];
+ callback(renderer, rendererId);
+
+ }, this);
+
+ return true;
+ },
+
+ _disposePaneRenderers: function () {
+
+ if (!this._childRenderers) {
+ return;
+ }
+
+ this._rendererIds.forEach(function (rendererId) {
+ this._map.removeLayer(this._childRenderers[rendererId]);
+ this._childRenderers[rendererId] = undefined;
+ }, this);
+
+ this._childRenderers = undefined;
+ },
+
+ onRemove: function () {
+ this._disposePaneRenderers();
+ L.SplitPanesRenderer.prototype.onRemove.call(this);
+ },
+
+ getChildPosBounds: function (childRenderer) {
+ console.assert(typeof childRenderer.rendererId === 'string', 'Child renderer does not have a rendererId!');
+ var rendererId = childRenderer.rendererId;
+ var renderer = this._childRenderers[rendererId];
+ console.assert(renderer && L.stamp(renderer) === L.stamp(childRenderer), 'Child renderer does not belong to parent!');
+
+ var splitPos = this._splitPanesContext.getSplitPos();
+ var size = this._map.getSize();
+ var pixelOrigin = this._map.getPixelOrigin();
+ // Container coordinates.
+ var topLeft = new L.Point(0, 0);
+ // pos and boundPos should be in layer coordinates.
+ var pos = undefined;
+ var boundPos = undefined;
+
+ if (rendererId === 'fixed') {
+ // This is for displaying the pane-splitter horizontal/vertical lines.
+ // is always glued to (0, 0) of the document.
+ // The size is always the map's view size.
+ pos = this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+ boundPos = topLeft.subtract(pixelOrigin);
+ }
+ else if (rendererId === 'bottomright') {
+ // this is the default splitPane where are no visible splits (splitPos = (0, 0)).
+ topLeft = splitPos;
+ size = size.subtract(splitPos);
+ pos = this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+ boundPos = pos;
+ }
+ else if (rendererId === 'topleft') {
+ // is always glued to (0, 0) of the document.
+ size.x = splitPos.x - 1;
+ size.y = splitPos.y - 1;
+ pos = this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+ boundPos = topLeft.subtract(pixelOrigin);
+ }
+ else if (rendererId === 'topright') {
+ // is always glued to top (y = 0) of the document.
+ topLeft.x = splitPos.x;
+ size.x -= splitPos.x;
+ size.y = splitPos.y - 1;
+ pos = this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+ boundPos = new L.Point(pos.x, topLeft.y - pixelOrigin.y);
+ }
+ else if (rendererId === 'bottomleft') {
+ // is always glued to left (x = 0) of the document.
+ topLeft.y = splitPos.y;
+ size.y -= splitPos.y;
+ size.x = splitPos.x - 1;
+ pos = this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+ boundPos = new L.Point(topLeft.x - pixelOrigin.x, pos.y);
+ }
+ else {
+ console.error('unhandled rendererId : ' + rendererId);
+ }
+
+ var bounds = new L.Bounds(boundPos, boundPos.add(size));
+
+ return {
+ bounds: bounds,
+ position: pos,
+ };
+ },
+
+ getEvents: function () {
+ var events = {
+ splitposchanged: this._update
+ };
+
+ return events;
+ },
+
+ _update: function () {
+
+ this._forEachChildRenderer(function (renderer) {
+ renderer._update();
+ });
+ },
+
+ // methods below are called by vector layers implementations
+
+ _initPath: function (layer) {
+
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._initPath(layer);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._initPath(layer);
+ });
+ },
+
+ _initGroup: function (layer) {
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._initGroup(layer);
+ });
+ },
+
+ _fireMouseEvent: function () {
+ // child renderers listen for ['mouseenter', 'mouseout'], and it refires with additional info
+ // but these events have any listeners ? may be DomEvent.js generates other events ?
+
+ // TODO: make the child renderers call this and create the right ones.
+ },
+
+ _addGroup: function (layer) {
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._addGroup(layer);
+ });
+ },
+
+ _addPath: function (layer) {
+
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._addPath(layer);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._addPath(layer);
+ });
+ },
+
+ _removeGroup: function (layer) {
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._removeGroup(layer);
+ });
+ },
+
+ _removePath: function (layer) {
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._removePath(layer);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._removePath(layer);
+ });
+ },
+
+ // should not forward to children.
+ _updatePath: function (layer) {
+ layer._project();
+ layer._update();
+ },
+
+ _updateStyle: function (layer) {
+
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._updateStyle(layer);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._updateStyle(layer);
+ });
+ },
+
+ // enough to forward the _setPath for the actual path-node modification part.
+ _updatePoly: function (layer, closed) {
+ this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
+ },
+
+ // enough to forward the _setPath for the actual path-node modification part.
+ _updateCircle: function (layer) {
+ var p = layer._point;
+ var r = layer._radius;
+ var r2 = layer._radiusY || r;
+ var arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
+
+ // drawing a circle with two half-arcs
+ var d = layer._empty() ? 'M0 0' :
+ 'M' + (p.x - r) + ',' + p.y +
+ arc + (r * 2) + ',0 ' +
+ arc + (-r * 2) + ',0 ';
+
+ this._setPath(layer, d);
+ },
+
+ _setPath: function (layer, path) {
+
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._setPath(layer, path);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._setPath(layer, path);
+ });
+ },
+
+ // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
+ _bringToFront: function (layer) {
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._bringToFront(layer);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._bringToFront(layer);
+ });
+ },
+
+ _bringToBack: function (layer) {
+
+ if (layer.options.fixed === true) {
+ this._childRenderers['fixed']._bringToBack(layer);
+ return;
+ }
+
+ this._forEachPaneRenderer(function (paneRenderer) {
+ paneRenderer._bringToBack(layer);
+ });
+ },
+
+ intersectsBounds: function (pxBounds) {
+ for (var i = 0; i < this._rendererIds.length; ++i) {
+ var rendererId = this._rendererIds[i];
+ if (this._childRenderers[rendererId].intersectsBounds(pxBounds)) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ addContainerClass: function (className) {
+ L.DomUtil.addClass(this._container, className);
+ this._forEachChildRenderer(function (childRenderer) {
+ childRenderer.addContainerClass(className);
+ });
+ },
+
+ removeContainerClass: function (className) {
+ L.DomUtil.removeClass(this._container, className);
+ this._forEachChildRenderer(function (childRenderer) {
+ childRenderer.removeContainerClass(className);
+ });
+ },
+
+});
+
+L.splitPanesSVG = function (options) {
+ return new L.SplitPanesSVG(options);
+};
More information about the Libreoffice-commits
mailing list