[Libreoffice-commits] online.git: Branch 'distro/collabora/collabora-online-4' - loleaflet/css loleaflet/src
Marco Cecchetti (via logerrit)
logerrit at kemper.freedesktop.org
Wed Oct 2 23:03:41 UTC 2019
loleaflet/css/loleaflet.css | 18 -
loleaflet/src/control/Control.ContextToolbar.js | 206 +++++++++++++-----
loleaflet/src/control/Control.MobileInput.js | 21 +
loleaflet/src/geo/LatLngBounds.js | 28 ++
loleaflet/src/layer/marker/Marker.js | 8
loleaflet/src/layer/tile/TileLayer.js | 26 ++
loleaflet/src/map/handler/Map.TouchGesture.js | 264 +++++++++++++++++++++---
7 files changed, 478 insertions(+), 93 deletions(-)
New commits:
commit 1020f07794ad85f96e129f60a4b4ed118417ae06
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Wed Oct 2 15:31:39 2019 +0200
Commit: Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Oct 3 01:03:22 2019 +0200
loleaflet: improve context toolbars in the iOS App
On double tap on a word, the word is selected and a 'Cut|Copy|Paste'
toolbar is shown.
On double tap on an empty line or after a space a 'Paste' toolbar is
shown and the text cursor is moved to tap point.
On press on a not selected word the text cursor is moved to the touch
point and a 'Paste' toolbar is shown.
On press on a the selected cell a 'Paste' toolbar is shown.
On press on the text cursor handle a 'Paste' toolbar is shown.
The 'Paste' entry is shown only if there is a text content that can be
pasted. An empty toolbar is never shown.
On single tap the context toolbar is removed.
On panning, pinching, dragging the text cursor handle or dragging the
text selection handles a context toolbar (if active) is hidden and
then shown again at the correct position when the action is over.
On typing and on lost focus the context toolbar is removed.
The context toolbar is placed so that to be centered wrt the text
cursor or the north center of the text selection bounds.
L.Control.ContextToolbar has been generalized so that now can be
easily populated with any entry and extended to support any command.
Change-Id: Ia112a491d952ed5b32c6dfbb600fc414ac1181e3
Reviewed-on: https://gerrit.libreoffice.org/80040
Reviewed-by: Tor Lillqvist <tml at collabora.com>
Tested-by: Tor Lillqvist <tml at collabora.com>
diff --git a/loleaflet/css/loleaflet.css b/loleaflet/css/loleaflet.css
index 8589c811c..6796417c4 100644
--- a/loleaflet/css/loleaflet.css
+++ b/loleaflet/css/loleaflet.css
@@ -448,15 +448,15 @@ body {
background: url('images/lc_rejecttrackedchange.svg') no-repeat center !important;
}
-.loleaflet-context-copy {
+.loleaflet-context-copy-button {
background: url('images/lc_copy.svg') no-repeat center !important;
}
-.loleaflet-context-cut {
+.loleaflet-context-cut-button {
background: url('images/lc_cut.svg') no-repeat center !important;
}
-.loleaflet-context-paste {
+.loleaflet-context-paste-button {
background: url('images/lc_paste.svg') no-repeat center !important;
}
@@ -473,7 +473,7 @@ body {
.loleaflet-ios-context-button {
background-color: black;
- height: 40px;
+ height: 36px;
}
.loleaflet-ios-context-left {
@@ -482,15 +482,15 @@ body {
border-bottom-left-radius: 10px;
}
-.loleaflet-ios-context-first-and-middle-entry {
- padding-left: 8px;
- padding-right: 8px;
+.loleaflet-ios-context-first-entry, .loleaflet-ios-context-middle-entry {
+ padding-left: 10px;
+ padding-right: 10px;
border-right: 1px solid white;
}
.loleaflet-ios-context-last-entry {
- padding-left: 8px;
- padding-right: 8px;
+ padding-left: 10px;
+ padding-right: 10px;
}
.loleaflet-ios-context-right {
diff --git a/loleaflet/src/control/Control.ContextToolbar.js b/loleaflet/src/control/Control.ContextToolbar.js
index 8d97e29c0..1236906d3 100644
--- a/loleaflet/src/control/Control.ContextToolbar.js
+++ b/loleaflet/src/control/Control.ContextToolbar.js
@@ -7,100 +7,192 @@
L.Control.ContextToolbar = L.Control.extend({
options: {
position: 'topleft',
- item: ''
+ },
+
+ statics: {
+ TOOLBARS: {
+ 'TEXT_CURSOR_TOOLBAR': ['Paste'],
+ 'TEXT_SELECTION_TOOLBAR': ['Cut', 'Copy', 'Paste']
+ }
},
initialize: function (options) {
L.setOptions(this, options);
+
},
onAdd: function () {
+ // console.log('==> ContextToolbar.onAdd');
if (!this._container) {
this._initLayout();
}
- if (this.options.item === 'paste') {
- this._paste.style.display = '';
- this._cut.style.display = 'none';
- this._copy.style.display = 'none';
- }
-
- this._container.style.visibility = 'hidden';
+ this.hide();
return this._container;
},
onRemove: function () {
- this._paste.style.display = '';
- this._cut.style.display = '';
- this._copy.style.display = '';
- this.options.item = '';
+ if (this._bar)
+ // remove all previous entries
+ L.DomUtil.empty(this._bar);
+ this.hide();
+ this._latlng = null;
+ // console.log('==> ContextToolbar.onRemove');
+ },
+
+ isInitialized: function () {
+ return !!this._bar.firstChild;
+ },
+
+ isVisible: function () {
+ return this._container && this._container.style.visibility === '';
+ },
+
+ hide: function () {
+ this._container.style.visibility = 'hidden';
+ },
+
+ show: function () {
+ this._container.style.visibility = '';
+ },
+
+ _createEntry: function (platform, command, noText, pos) {
+ if (!command || command.length === 0)
+ return;
+
+ var tagTd = 'td';
+ var entryTag = command.toLowerCase();
+ var platformFragment = (platform && platform.length) ? '-' + platform + '-' : '';
+ pos = (pos && pos.length) ? pos : 'middle';
+ var flagEntryClass = 'loleaflet-context-' + entryTag; // this class is used only for retrieving the command type
+ var genericButtonClass = 'loleaflet' + platformFragment + 'context-button';
+ var specificButtonClass = 'loleaflet' + platformFragment + 'context-' + entryTag + '-button';
+ var posEntryClass = 'loleaflet' + platformFragment + 'context-' + pos + '-entry';
+ var classList = flagEntryClass + ' ' + genericButtonClass + ' ' + specificButtonClass + ' ' + posEntryClass;
+ var entry = L.DomUtil.create(tagTd, classList, this._bar);
+ if (!noText) {
+ // get locale name for the command
+ entry.innerHTML = _UNO('.uno:' + command);
+ }
+ var stopEvents = 'touchstart touchmove touchend mousedown mousemove mouseout mouseover mouseup mousewheel click scroll';
+
+ L.DomEvent.on(entry, stopEvents, L.DomEvent.stopPropagation)
+ .on(entry, 'mousedown', this.onMouseDown, this)
+ .on(entry, 'mouseup', this.onMouseUp, this);
+
+ return entry;
+ },
+
+ _entryIs: function(name, entry) {
+ var flagEntryClass = 'loleaflet-context-' + name;
+ return L.DomUtil.hasClass(entry, flagEntryClass);
},
_initLayout: function () {
+ // create an empty toolbar, it will be populated with command entries later
if (window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp)
this._container = L.DomUtil.create('div', 'loleaflet-ios-context-toolbar');
else
this._container = L.DomUtil.create('div', 'loleaflet-context-toolbar');
- var tagTd = 'td',
- onUp = 'mouseup',
- onDown = 'mousedown',
- stopEvents = 'touchstart touchmove touchend mousedown mousemove mouseout mouseover mouseup mousewheel click scroll',
- container;
-
+ var container;
if (window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp)
container = L.DomUtil.create('table', 'loleaflet-ios-context-table', this._container);
else
container = L.DomUtil.create('table', 'loleaflet-context-table', this._container);
- var tbody = L.DomUtil.create('tbody', '', container),
- tr = L.DomUtil.create('tr', '', tbody);
+ var tbody = L.DomUtil.create('tbody', '', container);
+ this._bar = L.DomUtil.create('tr', '', tbody);
+ },
+
+ onAdded: function () {
+ this.show();
+ },
+
+ setEntries: function(commands) {
+ if (!this._bar)
+ return;
+
+ // commands is the name of a predefined toolbar ?
+ if (typeof commands === 'string') {
+ commands = L.Control.ContextToolbar.TOOLBARS[commands];
+ if (!commands)
+ return;
+ }
+
+ // remove all previous entries
+ L.DomUtil.empty(this._bar);
+
+ // check commands validity
+ var validCommands = [];
+ if (this._map && this._map._docLayer._internalCacheEmpty) {
+ for (var k = 0; k < commands.length; ++k) {
+ var cmd = commands[k];
+ if (cmd === 'Paste')
+ continue;
+ validCommands.push(cmd);
+ }
+ } else {
+ validCommands = commands;
+ }
+ if (!validCommands.length)
+ return;
+ var tagTd = 'td';
+ var platform = '';
+ var noText = true;
if (window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp) {
- this._leftroundedend = L.DomUtil.create(tagTd, 'loleaflet-ios-context-button loleaflet-ios-context-left', tr);
- this._cut = L.DomUtil.create(tagTd, 'loleaflet-ios-context-button loleaflet-ios-context-first-and-middle-entry loleaflet-ios-context-cut', tr);
- this._cut.innerHTML = _UNO('.uno:Cut');
- this._copy = L.DomUtil.create(tagTd, 'loleaflet-ios-context-button loleaflet-ios-context-first-and-middle-entry loleaflet-ios-context-copy', tr);
- this._copy.innerHTML = _UNO('.uno:Copy');
- this._paste = L.DomUtil.create(tagTd, 'loleaflet-ios-context-button loleaflet-ios-context-last-entry loleaflet-ios-context-paste', tr);
- this._paste.innerHTML = _UNO('.uno:Paste');
- this._rightroundedend = L.DomUtil.create(tagTd, 'loleaflet-ios-context-button loleaflet-ios-context-right', tr);
+ platform = 'ios';
+ noText = false;
+ this._leftroundedend = L.DomUtil.create(tagTd, 'loleaflet-ios-context-button loleaflet-ios-context-left', this._bar);
+ }
+
+ for (var i = 0; i < validCommands.length; ++i) {
+ var command = validCommands[i];
+ var pos;
+ if (i === validCommands.length - 1)
+ pos = 'last';
+ else if (i === 0)
+ pos = 'first';
+ else
+ pos = 'middle';
+ this._createEntry(platform, command, noText, pos);
}
- else {
- this._cut = L.DomUtil.create(tagTd, 'loleaflet-context-button loleaflet-context-cut', tr);
- this._copy = L.DomUtil.create(tagTd, 'loleaflet-context-button loleaflet-context-copy', tr);
- this._paste = L.DomUtil.create(tagTd, 'loleaflet-context-button loleaflet-context-paste', tr);
+
+ if (window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp) {
+ this._rightroundedend = L.DomUtil.create(tagTd, 'loleaflet-ios-context-button loleaflet-ios-context-right', this._bar);
}
- L.DomEvent.on(this._cut, stopEvents, L.DomEvent.stopPropagation)
- .on(this._cut, onDown, this.onMouseDown, this)
- .on(this._cut, onUp, this.onMouseUp, this);
- L.DomEvent.on(this._copy, stopEvents, L.DomEvent.stopPropagation)
- .on(this._copy, onDown, this.onMouseDown, this)
- .on(this._copy, onUp, this.onMouseUp, this);
- L.DomEvent.on(this._paste, stopEvents, L.DomEvent.stopPropagation)
- .on(this._paste, onDown, this.onMouseDown, this)
- .on(this._paste, onUp, this.onMouseUp, this);
},
- onAdded: function () {
- if (this._pos) {
+ setPosition: function (latlng) {
+ // hint: the toolbar should be populated earlier than set up the position
+ if (this._map && this._bar && this.isInitialized() && latlng) {
+ this._latlng = latlng;
+ var pos = this._map.project(latlng);
var maxBounds = this._map.getPixelBounds();
var size = L.point(this._container.clientWidth,this._container.clientHeight);
- this._pos._add(L.point(-size.x / 2, -size.y));
- var bounds = new L.Bounds(this._pos, this._pos.add(size));
+ pos._add(L.point(-size.x / 2, -5 * size.y / 4));
+ var bounds = new L.Bounds(pos, pos.add(size));
if (!maxBounds.contains(bounds)) {
var offset = L.point(0, 0);
if (bounds.max.x > maxBounds.max.x) {
- offset.x = size.x;
+ offset.x = bounds.max.x - maxBounds.max.x;
}
-
if (bounds.max.y > maxBounds.max.y) {
- offset.y = size.y;
+ offset.y = bounds.max.y - maxBounds.max.y;
+ }
+ pos._subtract(offset);
+ if (bounds.min.x < maxBounds.min.x) {
+ offset.x = maxBounds.min.x - bounds.min.x;
+ }
+ if (bounds.min.y < maxBounds.min.y) {
+ offset.y = maxBounds.min.y - bounds.min.y;
}
- this._pos._subtract(offset);
+ pos._add(offset);
}
- L.DomUtil.setPosition(this._container, this._pos);
+ var containerPoint = this._map.latLngToContainerPoint(this._map.unproject(pos));
+ L.DomUtil.setPosition(this._container, containerPoint);
+ this.show();
}
- this._container.style.visibility = '';
},
onMouseDown: function (e) {
@@ -111,17 +203,15 @@ L.Control.ContextToolbar = L.Control.extend({
onMouseUp: function (e) {
var target = e.target || e.srcElement;
-
- if (L.DomUtil.hasClass(target, 'loleaflet-context-cut') ||
- L.DomUtil.hasClass(target, 'loleaflet-ios-context-cut')) {
+ if (this._entryIs('cut', target)) {
this._map._socket.sendMessage('uno .uno:Cut');
+ this._map._docLayer._internalCacheEmpty = false;
}
- else if (L.DomUtil.hasClass(target, 'loleaflet-context-copy') ||
- L.DomUtil.hasClass(target, 'loleaflet-ios-context-copy')) {
+ else if (this._entryIs('copy', target)) {
this._map._socket.sendMessage('uno .uno:Copy');
+ this._map._docLayer._internalCacheEmpty = false;
}
- else if (L.DomUtil.hasClass(target, 'loleaflet-context-paste') ||
- L.DomUtil.hasClass(target, 'loleaflet-ios-context-paste')) {
+ else if (this._entryIs('paste', target)) {
this._map._socket.sendMessage('uno .uno:Paste');
}
diff --git a/loleaflet/src/control/Control.MobileInput.js b/loleaflet/src/control/Control.MobileInput.js
index 6eddbe5e8..07ab7b520 100644
--- a/loleaflet/src/control/Control.MobileInput.js
+++ b/loleaflet/src/control/Control.MobileInput.js
@@ -19,6 +19,9 @@ L.Control.MobileInput = L.Control.extend({
draggable: true
});
+ if (window.ThisIsTheiOSApp) {
+ this._cursorHandler.on('dragstart', this.onDragStart, this);
+ }
this._cursorHandler.on('dragend', this.onDragEnd, this);
this._currentKeysDown = {};
this._ignoreKeypress = false;
@@ -40,10 +43,17 @@ L.Control.MobileInput = L.Control.extend({
this._map.fire('input.press', this._cursorHandler.getLatLng());
},
+ onDragStart: function () {
+ this._map.fire('input.dragstart', this._cursorHandler.getLatLng());
+ },
+
onDragEnd: function () {
var mousePos = this._map._docLayer._latLngToTwips(this._cursorHandler.getLatLng());
this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0);
this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0);
+ if (window.ThisIsTheiOSApp) {
+ this._map.fire('input.dragend', this._cursorHandler.getLatLng());
+ }
},
onGotFocus: function () {
@@ -74,6 +84,10 @@ L.Control.MobileInput = L.Control.extend({
}
this._map.removeLayer(this._cursorHandler);
}
+ if (window.ThisIsTheiOSApp) {
+ // when the focus is lost remove the context toolbar
+ this._map.fire('input.blur');
+ }
},
focus: function (focus) {
@@ -185,6 +199,13 @@ L.Control.MobileInput = L.Control.extend({
docLayer._postKeyboardEvent('input', charCode, unoKeyCode);
this._lastInput = unoKeyCode;
}
+ if (window.ThisIsTheiOSApp) {
+ // when the user start typing remove the context toolbar
+ if (this._map.touchGesture._toolbar && this._map.touchGesture._toolbarAdded) {
+ this._map.touchGesture._toolbar.remove();
+ this._map.touchGesture._toolbarAdded = null;
+ }
+ }
}
else if (this._isMobileSafariOriOSApp &&
e.type === 'keypress') {
diff --git a/loleaflet/src/geo/LatLngBounds.js b/loleaflet/src/geo/LatLngBounds.js
index e6f7396a2..8ef7ceb4f 100644
--- a/loleaflet/src/geo/LatLngBounds.js
+++ b/loleaflet/src/geo/LatLngBounds.js
@@ -60,7 +60,7 @@ L.LatLngBounds.prototype = {
new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
},
- // extend the bounds by a percentage
+ // extend the bounds vertically by a percentage
padVertically: function (bufferRatio) { // (Number) -> LatLngBounds
var sw = this._southWest,
ne = this._northEast,
@@ -71,6 +71,17 @@ L.LatLngBounds.prototype = {
new L.LatLng(ne.lat + heightBuffer, ne.lng));
},
+ // extend the bounds horizontally by a percentage
+ padHorizontally: function (bufferRatio) { // (Number) -> LatLngBounds
+ var sw = this._southWest,
+ ne = this._northEast,
+ widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
+
+ return new L.LatLngBounds(
+ new L.LatLng(sw.lat, sw.lng - widthBuffer),
+ new L.LatLng(ne.lat, ne.lng + widthBuffer));
+ },
+
getCenter: function () { // -> LatLng
return new L.LatLng(
(this._southWest.lat + this._northEast.lat) / 2,
@@ -145,6 +156,21 @@ L.LatLngBounds.prototype = {
return latIntersects && lngIntersects;
},
+ intersection: function (bounds) { // (LatLngBounds)
+ bounds = L.latLngBounds(bounds);
+
+ var sw = this._southWest,
+ ne = this._northEast,
+ sw2 = bounds.getSouthWest(),
+ ne2 = bounds.getNorthEast(),
+ nelat = Math.min(ne.lat, ne2.lat),
+ nelng = Math.min(ne.lng, ne2.lng),
+ swlat = Math.max(sw.lat, sw2.lat),
+ swlng = Math.max(sw.lng, sw2.lng);
+
+ return L.latLngBounds(L.latLng(swlat, swlng), L.latLng(nelat, nelng));
+ },
+
toBBoxString: function () {
return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
},
diff --git a/loleaflet/src/layer/marker/Marker.js b/loleaflet/src/layer/marker/Marker.js
index 3db3dbfd9..b0229c102 100644
--- a/loleaflet/src/layer/marker/Marker.js
+++ b/loleaflet/src/layer/marker/Marker.js
@@ -113,6 +113,14 @@ L.Marker = L.Layer.extend({
return this;
},
+ getBounds: function () {
+ if (!this._map)
+ return null;
+ var topLeftPx = this._map.project(this._latlng);
+ var bottomRightPx = L.point(topLeftPx.x + this._icon.clientWidth, topLeftPx.y + this._icon.clientHeight);
+ return new L.LatLngBounds(this._latlng, this._map.unproject(bottomRightPx));
+ },
+
_initIcon: function () {
var options = this.options,
classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 2d324784e..8b60f8ef4 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -211,6 +211,7 @@ L.TileLayer = L.GridLayer.extend({
this._levels = {};
this._tiles = {};
this._tileCache = {};
+ this._internalCacheEmpty = true;
this._map._socket.sendMessage('commandvalues command=.uno:LanguageStatus');
this._map._socket.sendMessage('commandvalues command=.uno:ViewAnnotations');
var that = this;
@@ -311,6 +312,9 @@ L.TileLayer = L.GridLayer.extend({
for (var key in this._selectionHandles) {
this._selectionHandles[key].on('drag dragend', this._onSelectionHandleDrag, this);
+ if (window.ThisIsTheiOSApp) {
+ this._selectionHandles[key].on('dragstart', this._onSelectionHandleDrag, this);
+ }
}
this._cellResizeMarkerStart.on('dragstart drag dragend', this._onCellResizeMarkerDrag, this);
@@ -1287,6 +1291,9 @@ L.TileLayer = L.GridLayer.extend({
weight: 2,
opacity: 0.25});
this._selections.addLayer(selection);
+ if (window.ThisIsTheiOSApp) {
+ this._map.fire('textselected', null);
+ }
if (this._selectionContentRequest) {
clearTimeout(this._selectionContentRequest);
}
@@ -2198,6 +2205,19 @@ L.TileLayer = L.GridLayer.extend({
// Update dragged text selection.
_onSelectionHandleDrag: function (e) {
+ if (window.ThisIsTheiOSApp) {
+ if (e.type === 'dragstart') {
+ if (this._map.touchGesture._toolbar && this._map.touchGesture._toolbar.isVisible()) {
+ this._map.touchGesture._toolbar.remove();
+ this._map.touchGesture._toolbarAdded = null;
+ }
+ if (this._map.touchGesture._toolbar) {
+ this._map.touchGesture._newTextSelection = false;
+ }
+ return;
+ }
+ }
+
if (e.type === 'drag') {
e.target.isDragged = true;
@@ -2251,6 +2271,12 @@ L.TileLayer = L.GridLayer.extend({
else if (this._selectionHandles.end === e.target) {
this._postSelectTextEvent('end', aPos.x, aPos.y);
}
+
+ if (window.ThisIsTheiOSApp) {
+ if (e.type === 'dragend') {
+ this._map.fire('textselection.dragend', e.target.getLatLng());
+ }
+ }
},
// Update dragged text selection.
diff --git a/loleaflet/src/map/handler/Map.TouchGesture.js b/loleaflet/src/map/handler/Map.TouchGesture.js
index d222d5a89..5d143f3f4 100644
--- a/loleaflet/src/map/handler/Map.TouchGesture.js
+++ b/loleaflet/src/map/handler/Map.TouchGesture.js
@@ -14,7 +14,9 @@ L.Map.TouchGesture = L.Handler.extend({
CURSOR: 2,
GRAPHIC: 4,
MARKER: 8,
- TABLE: 16
+ TABLE: 16,
+ TEXT_CURSOR_HANDLE: 32,
+ TEXT_SELECTION_HANDLE: 64
},
initialize: function (map) {
@@ -89,8 +91,14 @@ L.Map.TouchGesture = L.Handler.extend({
this._hammer.on('pinchmove', L.bind(this._onPinch, this));
this._hammer.on('pinchend', L.bind(this._onPinchEnd, this));
this._hammer.on('tripletap', L.bind(this._onTripleTap, this));
- if (window.ThisIsTheiOSApp)
+ if (window.ThisIsTheiOSApp) {
this._map.on('input.press', this._onInputPressiOSOnly, this);
+ this._map.on('input.dragstart', this._onInputDragStartiOSOnly, this);
+ this._map.on('input.dragend', this._onInputDragEndiOSOnly, this);
+ this._map.on('input.blur', this._onInputLostFocusiOSOnly, this);
+ this._map.on('textselected', this._onTextSelectediOSOnly, this);
+ this._map.on('textselection.dragend', this._onTextSelectionHandleDragEndiOSOnly, this);
+ }
this._map.on('updatepermission', this._onPermission, this);
this._onPermission({perm: this._map._permission});
},
@@ -131,6 +139,16 @@ L.Map.TouchGesture = L.Handler.extend({
this._marker = this._map._docLayer._graphicMarker.transform.getMarker(layerPoint);
}
+ var cursorHandleBounds;
+ if (this._map._clipboardContainer._cursorHandler)
+ cursorHandleBounds = this._map._clipboardContainer._cursorHandler.getBounds();
+ var startTextSelectionHandleBounds;
+ if (this._map._docLayer._selectionHandles['start'])
+ startTextSelectionHandleBounds = this._map._docLayer._selectionHandles['start'].getBounds();
+ var endTextSelectionHandleBounds;
+ if (this._map._docLayer._selectionHandles['end'])
+ endTextSelectionHandleBounds = this._map._docLayer._selectionHandles['end'].getBounds();
+
if (this._marker) {
this._state = L.Map.TouchGesture.MARKER;
} else if (this._map._docLayer._graphicMarker && this._map._docLayer._graphicMarker.getBounds().contains(latlng)) {
@@ -140,9 +158,15 @@ L.Map.TouchGesture = L.Handler.extend({
this._state = L.Map.TouchGesture.GRAPHIC;
} else if (this._map._docLayer._cellCursor && this._map._docLayer._cellCursor.contains(latlng)) {
this._state = L.Map.TouchGesture.CURSOR;
+ } else if (cursorHandleBounds && cursorHandleBounds.padHorizontally(0.2).contains(latlng)) {
+ this._state = L.Map.TouchGesture.TEXT_CURSOR_HANDLE;
+ } else if ((startTextSelectionHandleBounds && startTextSelectionHandleBounds.padHorizontally(0.2).contains(latlng))
+ || (endTextSelectionHandleBounds && endTextSelectionHandleBounds.padHorizontally(0.2).contains(latlng))) {
+ this._state = L.Map.TouchGesture.TEXT_SELECTION_HANDLE;
} else {
this._state = L.Map.TouchGesture.MAP;
}
+ // console.log('==> _onHammer: _state: ' + this._state);
}
if (e.isLast && this._state !== L.Map.TouchGesture.MAP) {
@@ -162,6 +186,30 @@ L.Map.TouchGesture = L.Handler.extend({
}
},
+ _addContextToolbar: function (commands, latlng, timeStamp) {
+ this._toolbar.remove();
+ this._toolbar.addTo(this._map);
+ this._toolbar.setEntries(commands);
+ this._toolbar.setPosition(latlng);
+ this._toolbarAdded = timeStamp;
+ },
+
+ _getTextSelectionNorthCenter: function () {
+ var result;
+ var selections = this._map._docLayer._selections;
+ if (selections) {
+ var layers = selections.getLayers();
+ if (layers && layers.length === 1) {
+ var mapBounds = this._map.getBounds();
+ var bounds = layers[0].getBounds();
+ bounds = mapBounds.intersection(bounds);
+ result = bounds.getCenter();
+ result.lat = bounds.getNorth();
+ }
+ }
+ return result;
+ },
+
_onPress: function (e) {
var point = e.pointers[0],
containerPoint = this._map.mouseEventToContainerPoint(point),
@@ -169,28 +217,81 @@ L.Map.TouchGesture = L.Handler.extend({
latlng = this._map.layerPointToLatLng(layerPoint),
mousePos = this._map._docLayer._latLngToTwips(latlng);
+ var docLayer = this._map._docLayer;
+
if (window.ThisIsTheiOSApp) {
- // console.log('==> ' + e.timeStamp);
- if (!this._toolbar._map && this._map._docLayer.containsSelection(latlng)) {
- this._toolbar._pos = containerPoint;
- // console.log('==> Adding context toolbar ' + e.timeStamp);
- this._toolbar.addTo(this._map);
- this._toolbarAdded = e.timeStamp;
- } else if (this._toolbarAdded && e.timeStamp - this._toolbarAdded >= 1000) {
- // console.log('==> Removing context toolbar ' + e.timeStamp);
- this._toolbar.remove();
- this._map._contextMenu._onMouseDown({originalEvent: e.srcEvent});
- // send right click to trigger context menus
- this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 4, 0);
- this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 4, 0);
+ // if user has pressed on cursor or text selection handles don't perform any action
+ // so far we skip also press actions on graphics and cell cursor
+ // we check focus in order to not get unexpected behavior with open dialogs
+ var hasPressedOnCellCursor = this._state === L.Map.TouchGesture.CURSOR;
+ if ((this._state === L.Map.TouchGesture.MAP && this._map.hasFocus()) || hasPressedOnCellCursor) {
+ // console.log('==> onPress: ' + e.timeStamp);
+ // no text selected
+ if (!docLayer.containsSelection(latlng) && !hasPressedOnCellCursor) {
+ // I see several press events generated for the same press action, try to skip the redundant ones.
+ if (!this._prevPressContainerPoint || !(this._contextToolbarTimeout && containerPoint.equals(this._prevPressContainerPoint))) {
+ // we can skip when the toolbar is already active and user has pressed at the text cursor position
+ if (this._toolbar.isVisible() && docLayer._visibleCursor.padHorizontally(0.2).contains(latlng))
+ return;
+ // place the text cursor where user pressed
+ docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0);
+ docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0);
+
+ var that = this;
+ var timeout = 300;
+ var prevCursorPos = this._map.latLngToContainerPoint(docLayer._visibleCursor.getNorthEast());
+
+ var setContextToolbar = function (n) {
+ var toolbarUpdated = false;
+ // is the text cursor visible ?
+ if (docLayer._isCursorVisible && !docLayer._isEmptyRectangle(docLayer._visibleCursor) && docLayer._cursorMarker) {
+ var posLatLng = docLayer._visibleCursor.getNorthEast();
+ var pos = that._map.latLngToContainerPoint(posLatLng);
+ // do the client get the new cursor position ? if client doesn't, let's try to re-schedule this routine
+ if (!pos.equals(prevCursorPos) || !that._toolbar.isVisible()) {
+ var commands = 'TEXT_CURSOR_TOOLBAR';
+ // console.log('==> onPress: Adding context toolbar ' + Date.now() + ', call: ' + n);
+ that._addContextToolbar(commands, posLatLng, Date.now());
+ toolbarUpdated = true;
+ }
+ }
+ // if the toolbar has not been update re-schedule this routine
+ if (!toolbarUpdated && n < 5) {
+ n += 1;
+ that._contextToolbarTimeout = setTimeout(setContextToolbar, n * timeout, n);
+ } else {
+ that._contextToolbarTimeout = null;
+ }
+ };
+
+ if (this._contextToolbarTimeout)
+ clearTimeout(this._contextToolbarTimeout);
+ this._contextToolbarTimeout = setTimeout(setContextToolbar, timeout, 1);
+ }
+ // if some text is selected and the toolbar is active, the user wants to trigger the context menu
+ } else if (hasPressedOnCellCursor && !this._toolbar.isVisible()) {
+ if (!this._prevPressContainerPoint || !(this._toolbar.isVisible() && containerPoint.equals(this._prevPressContainerPoint))) {
+ var commands = 'TEXT_CURSOR_TOOLBAR';
+ // console.log('==> onPress: Adding context toolbar ' + e.timeStamp);
+ this._addContextToolbar(commands, latlng, e.timeStamp);
+ }
+ } else if (this._toolbar.isVisible() && e.timeStamp - this._toolbarAdded >= 1000) {
+ // console.log('==> onPress: Removing context toolbar ' + e.timeStamp);
+ this._toolbar.remove();
+ this._toolbarAdded = null;
+ this._map._contextMenu._onMouseDown({originalEvent: e.srcEvent});
+ // send right click to trigger context menus
+ docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 4, 0);
+ docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 4, 0);
+ }
}
+ this._prevPressContainerPoint = containerPoint;
} else {
this._map._contextMenu._onMouseDown({originalEvent: e.srcEvent});
// send right click to trigger context menus
- this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 4, 0);
- this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 4, 0);
+ docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 4, 0);
+ docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 4, 0);
}
-
e.preventDefault();
},
@@ -201,8 +302,10 @@ L.Map.TouchGesture = L.Handler.extend({
latlng = this._map.layerPointToLatLng(layerPoint),
mousePos = this._map._docLayer._latLngToTwips(latlng);
- if (window.ThisIsTheiOSApp)
+ if (window.ThisIsTheiOSApp) {
this._toolbar.remove();
+ this._toolbarAdded = null;
+ }
this._map._contextMenu._onMouseDown({originalEvent: e.srcEvent});
this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0);
this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0);
@@ -217,8 +320,50 @@ L.Map.TouchGesture = L.Handler.extend({
latlng = this._map.layerPointToLatLng(layerPoint),
mousePos = this._map._docLayer._latLngToTwips(latlng);
- this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2, 1, 0);
- this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2, 1, 0);
+ var docLayer = this._map._docLayer;
+
+ if (window.ThisIsTheiOSApp) {
+ this._newTextSelection = false;
+ }
+ docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2, 1, 0);
+ docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2, 1, 0);
+
+ if (window.ThisIsTheiOSApp) {
+ var that = this;
+ var timeout = 300;
+ var prevCursorPos = this._map.latLngToContainerPoint(docLayer._visibleCursor.getSouthWest());
+
+ var setContextToolbar = function (n) {
+ var canBeUpdated = false;
+ var commands, pos, posLatLng;
+ if (that._newTextSelection) {
+ commands = 'TEXT_SELECTION_TOOLBAR';
+ posLatLng = that._getTextSelectionNorthCenter() || latlng;
+ canBeUpdated = true;
+ } else if (docLayer._isCursorVisible && !docLayer._isEmptyRectangle(docLayer._visibleCursor) && docLayer._cursorMarker) {
+ posLatLng = docLayer._visibleCursor.getNorthEast();
+ pos = that._map.latLngToContainerPoint(posLatLng);
+ if (!pos.equals(prevCursorPos) || !that._toolbar.isVisible()) {
+ commands = 'TEXT_CURSOR_TOOLBAR';
+ canBeUpdated = true;
+ }
+ }
+ if (!canBeUpdated && n < 5) {
+ n += 1;
+ that._contextToolbarTimeout = setTimeout(setContextToolbar, n * timeout, n);
+ } else {
+ if (canBeUpdated) {
+ // console.log('==> onDoubleTap: Adding context toolbar ' + Date.now() + ', call: ' + n);
+ that._addContextToolbar(commands, posLatLng, Date.now());
+ }
+ that._contextToolbarTimeout = null;
+ }
+ };
+
+ if (this._contextToolbarTimeout)
+ clearTimeout(this._contextToolbarTimeout);
+ this._contextToolbarTimeout = setTimeout(setContextToolbar, timeout, 1);
+ }
},
_onTripleTap: function (e) {
@@ -239,6 +384,10 @@ L.Map.TouchGesture = L.Handler.extend({
latlng = this._map.layerPointToLatLng(layerPoint),
mousePos = this._map._docLayer._latLngToTwips(latlng);
+ if (window.ThisIsTheiOSApp) {
+ this._toolbar.hide();
+ }
+
var originalCellCursor = this._map._docLayer._cellCursor;
var increaseRatio = 0.40;
var increasedCellCursor = null;
@@ -310,6 +459,15 @@ L.Map.TouchGesture = L.Handler.extend({
latlng = this._map.layerPointToLatLng(layerPoint),
mousePos = this._map._docLayer._latLngToTwips(latlng);
+ if (window.ThisIsTheiOSApp) {
+ if (this._toolbarAdded) {
+ // set to the previous position
+ this._toolbar.setPosition(this._toolbar._latlng);
+ this._toolbar.show();
+ this._toolbarAdded = Date.now();
+ }
+ }
+
if (this._state === L.Map.TouchGesture.MARKER) {
this._map._fireDOMEvent(this._map, e.srcEvent, 'mouseup');
} else if (this._state === L.Map.TouchGesture.GRAPHIC) {
@@ -328,6 +486,9 @@ L.Map.TouchGesture = L.Handler.extend({
if (this._map.getDocType() !== 'spreadsheet') {
this._pinchStartCenter = {x: e.center.x, y: e.center.y};
}
+ if (window.ThisIsTheiOSApp) {
+ this._toolbar.hide();
+ }
},
_onPinch: function (e) {
@@ -349,16 +510,69 @@ L.Map.TouchGesture = L.Handler.extend({
var oldZoom = this._map.getZoom(),
zoomDelta = this._zoom - oldZoom,
finalZoom = this._map._limitZoom(zoomDelta > 0 ? Math.ceil(this._zoom) : Math.floor(this._zoom));
+ if (this._center) {
+ this._map._animateZoom(this._center, finalZoom, true, true);
+ }
+ }
- this._map._animateZoom(this._center, finalZoom, true, true);
+ if (window.ThisIsTheiOSApp) {
+ if (this._toolbarAdded) {
+ this._toolbar.setPosition(this._toolbar._latlng);
+ this._toolbar.show();
+ this._toolbarAdded = Date.now();
+ }
}
},
_onInputPressiOSOnly: function (e) {
- var pos = this._map.latLngToContainerPoint(e);
+ if (this._toolbar.isVisible())
+ return;
+ var posLatLng = this._map._docLayer._visibleCursor.getNorthEast() || L.latLng(e);
+ this._addContextToolbar('TEXT_CURSOR_TOOLBAR', posLatLng, Date.now());
+ },
+
+ _onInputDragStartiOSOnly: function () {
+ this._toolbar.hide();
+ // not set this._toolbarAdded to null it's checked on drag end
+ },
+
+ _onInputDragEndiOSOnly: function (e) {
+ if (!this._toolbarAdded)
+ return; // show the toolbar only if it was already active before starting dragging
+ var posLatLng = L.latLng(e);
+ this._toolbar.setPosition(posLatLng);
+ this._toolbar.show();
+ this._toolbarAdded = Date.now();
+ },
+
+ _onInputLostFocusiOSOnly: function () {
+ // remove the toolbar when a dialog pops up
+ if (!this._toolbar.isVisible())
+ return;
this._toolbar.remove();
- this._toolbar._pos = pos;
- this._toolbar.addTo(this._map);
+ this._toolbarAdded = null;
+ },
+
+ _onTextSelectionHandleDragEndiOSOnly: function (e) {
+ var posLatLng = L.latLng(e);
+ var that = this;
+ var timeout = 300;
+
+ var setContextToolbar = function (n) {
+ if (that._newTextSelection) {
+ posLatLng = that._getTextSelectionNorthCenter() || posLatLng;
+ that._addContextToolbar('TEXT_SELECTION_TOOLBAR', posLatLng, Date.now());
+ } else if (n < 5) {
+ n += 1;
+ setTimeout(setContextToolbar, n * timeout, n);
+ }
+ };
+
+ setTimeout(setContextToolbar, timeout, 1);
+ },
+
+ _onTextSelectediOSOnly: function () {
+ this._newTextSelection = true;
},
_constructFakeEvent: function (evt, type) {
More information about the Libreoffice-commits
mailing list