[Libreoffice-commits] online.git: kit/ChildSession.cpp kit/ChildSession.hpp loleaflet/css loleaflet/src wsd/ClientSession.cpp
Marco Cecchetti (via logerrit)
logerrit at kemper.freedesktop.org
Thu Feb 6 14:58:57 UTC 2020
kit/ChildSession.cpp | 61 +++++-
kit/ChildSession.hpp | 2
loleaflet/css/selectionMarkers.css | 2
loleaflet/src/control/Control.LokDialog.js | 253 +++++++++++++++++++++++++++--
wsd/ClientSession.cpp | 1
5 files changed, 290 insertions(+), 29 deletions(-)
New commits:
commit a46fa588b1759c5114937eec2824cbabdbf2082b
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
AuthorDate: Thu Feb 6 11:28:00 2020 +0100
Commit: Marco Cecchetti <marco.cecchetti at collabora.com>
CommitDate: Thu Feb 6 15:58:36 2020 +0100
calc: formula input bar: adding support for text selection handles
Change-Id: I6bc276945a7fd33f1358a3aa82ce0e7f45237771
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/88090
Reviewed-by: Marco Cecchetti <marco.cecchetti at collabora.com>
Tested-by: Marco Cecchetti <marco.cecchetti at collabora.com>
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index 58b420eaf..d45891b58 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -285,6 +285,7 @@ bool ChildSession::_handleInput(const char *buffer, int length)
tokens[0] == "windowgesture" ||
tokens[0] == "uno" ||
tokens[0] == "selecttext" ||
+ tokens[0] == "windowselecttext" ||
tokens[0] == "selectgraphic" ||
tokens[0] == "resetselection" ||
tokens[0] == "saveas" ||
@@ -379,7 +380,11 @@ bool ChildSession::_handleInput(const char *buffer, int length)
}
else if (tokens[0] == "selecttext")
{
- return selectText(buffer, length, tokens);
+ return selectText(buffer, length, tokens, LokEventTargetEnum::Document);
+ }
+ else if (tokens[0] == "windowselecttext")
+ {
+ return selectText(buffer, length, tokens, LokEventTargetEnum::Window);
}
else if (tokens[0] == "selectgraphic")
{
@@ -1478,25 +1483,55 @@ bool ChildSession::unoCommand(const char* /*buffer*/, int /*length*/, const std:
return true;
}
-bool ChildSession::selectText(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+bool ChildSession::selectText(const char* /*buffer*/, int /*length*/,
+ const std::vector<std::string>& tokens,
+ const LokEventTargetEnum target)
{
+ std::string swap;
+ unsigned winId = 0;
int type, x, y;
- if (tokens.size() != 4 ||
- !getTokenKeyword(tokens[1], "type",
- {{"start", LOK_SETTEXTSELECTION_START},
- {"end", LOK_SETTEXTSELECTION_END},
- {"reset", LOK_SETTEXTSELECTION_RESET}},
- type) ||
- !getTokenInteger(tokens[2], "x", x) ||
- !getTokenInteger(tokens[3], "y", y))
+ if (target == LokEventTargetEnum::Window)
{
- sendTextFrame("error: cmd=selecttext kind=syntax");
- return false;
+ if (tokens.size() != 5 ||
+ !getTokenUInt32(tokens[1], "id", winId) ||
+ !getTokenString(tokens[2], "swap", swap) ||
+ (swap != "true" && swap != "false") ||
+ !getTokenInteger(tokens[3], "x", x) ||
+ !getTokenInteger(tokens[4], "y", y))
+ {
+ LOG_ERR("error: cmd=windowselecttext kind=syntax");
+ return false;
+ }
+ }
+ else if (target == LokEventTargetEnum::Document)
+ {
+ if (tokens.size() != 4 ||
+ !getTokenKeyword(tokens[1], "type",
+ {{"start", LOK_SETTEXTSELECTION_START},
+ {"end", LOK_SETTEXTSELECTION_END},
+ {"reset", LOK_SETTEXTSELECTION_RESET}},
+ type) ||
+ !getTokenInteger(tokens[2], "x", x) ||
+ !getTokenInteger(tokens[3], "y", y))
+ {
+ sendTextFrame("error: cmd=selecttext kind=syntax");
+ return false;
+ }
}
getLOKitDocument()->setView(_viewId);
- getLOKitDocument()->setTextSelection(type, x, y);
+ switch (target)
+ {
+ case LokEventTargetEnum::Document:
+ getLOKitDocument()->setTextSelection(type, x, y);
+ break;
+ case LokEventTargetEnum::Window:
+ getLOKitDocument()->setWindowTextSelection(winId, swap == "true", x, y);
+ break;
+ default:
+ assert(false && "Unsupported select text target type");
+ }
return true;
}
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 2f677facd..4a64be709 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -269,7 +269,7 @@ private:
bool dialogEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
bool completeFunction(const char* buffer, int length, const std::vector<std::string>& tokens);
bool unoCommand(const char* buffer, int length, const std::vector<std::string>& tokens);
- bool selectText(const char* buffer, int length, const std::vector<std::string>& tokens);
+ bool selectText(const char* buffer, int length, const std::vector<std::string>& tokens, const LokEventTargetEnum target);
bool selectGraphic(const char* buffer, int length, const std::vector<std::string>& tokens);
bool renderWindow(const char* buffer, int length, const std::vector<std::string>& tokens);
bool resizeWindow(const char* buffer, int length, const std::vector<std::string>& tokens);
diff --git a/loleaflet/css/selectionMarkers.css b/loleaflet/css/selectionMarkers.css
index 2bf7f791c..70cd6d233 100644
--- a/loleaflet/css/selectionMarkers.css
+++ b/loleaflet/css/selectionMarkers.css
@@ -1,4 +1,5 @@
.leaflet-selection-marker-start {
+ position: absolute;
margin-left: -28px;
width: 30px;
height: 44px;
@@ -6,6 +7,7 @@
}
.leaflet-selection-marker-end {
+ position: absolute;
margin-left: -2px;
width: 30px;
height: 44px;
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index a16395258..9595d0496 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -158,6 +158,20 @@ L.Control.LokDialog = L.Control.extend({
return (id in this._dialogs) && this._dialogs[id].isCalcInputBar;
},
+ _isSelectionHandle: function(el) {
+ return L.DomUtil.hasClass(el, 'leaflet-selection-marker-start') ||
+ L.DomUtil.hasClass(el, 'leaflet-selection-marker-end');
+ },
+
+ _isSelectionHandleDragged: function() {
+ if (this._calcInputBar) {
+ var selectionInfo = this._calcInputBar.textSelection;
+ return (selectionInfo.startHandle && selectionInfo.startHandle.isDragged) ||
+ (selectionInfo.endHandle && selectionInfo.endHandle.isDragged);
+ }
+ return false;
+ },
+
// Given a prefixed dialog id like 'lokdialog-323', gives a raw id, 323.
_toIntId: function(id) {
if (typeof(id) === 'string')
@@ -412,8 +426,16 @@ L.Control.LokDialog = L.Control.extend({
var strId = this._toIntId(dlgId);
var selections = this._dialogs[strId].textSelection.rectangles;
L.DomUtil.empty(selections);
- if (!rectangles)
+ var handles = this._dialogs[strId].textSelection.handles;
+ var startHandle = this._dialogs[strId].textSelection.startHandle;
+ var endHandle = this._dialogs[strId].textSelection.endHandle;
+
+ if (!rectangles || rectangles.length < 1) {
+ if (!startHandle.isDragged && !endHandle.isDragged) {
+ L.DomUtil.empty(handles);
+ }
return;
+ }
for (var i = 0; i < rectangles.length; ++i) {
var container = L.DomUtil.create('div', 'leaflet-text-selection-container', selections);
@@ -424,6 +446,117 @@ L.Control.LokDialog = L.Control.extend({
L.DomUtil.setStyle(container, 'left', rect.x + 'px');
L.DomUtil.setStyle(container, 'top', rect.y + 'px');
}
+
+ var startRect = rectangles[0];
+ var endRect = rectangles[rectangles.length - 1];
+ if (startRect.width < 1 || endRect.width < 1)
+ return;
+ startRect = {x: startRect.x, y: startRect.y, width: 1, height: startRect.height};
+ endRect = {x: endRect.x + endRect.width - 1, y: endRect.y, width: 1, height: endRect.height};
+ var startPos = L.point(startRect.x, startRect.y + startRect.height);
+ startPos = startPos.subtract(L.point(0, 2));
+ startHandle.lastPos = startPos;
+ startHandle.rowHeight = startRect.height;
+ var endPos = L.point(endRect.x, endRect.y + endRect.height);
+ endPos = endPos.subtract(L.point(0, 2));
+ endHandle.lastPos = endPos;
+ endHandle.rowHeight = endRect.height;
+
+ if (!startHandle.isDragged) {
+ if (!handles.children || !handles.children[0])
+ handles.appendChild(startHandle);
+ //console.log('lokdialog: _updateTextSelection: startPos: x: ' + startPos.x + ', y: ' + startPos.y);
+ startHandle.pos = startPos;
+ L.DomUtil.setStyle(startHandle, 'left', startPos.x + 'px');
+ L.DomUtil.setStyle(startHandle, 'top', startPos.y + 'px');
+ }
+
+ if (!endHandle.isDragged) {
+ if (!handles.children || !handles.children[1])
+ handles.appendChild(endHandle);
+ //console.log('lokdialog: _updateTextSelection: endPos: x: ' + endPos.x + ', y: ' + endPos.y);
+ endHandle.pos = endPos;
+ L.DomUtil.setStyle(endHandle, 'left', endPos.x + 'px');
+ L.DomUtil.setStyle(endHandle, 'top', endPos.y + 'px');
+ }
+ },
+
+ _onSelectionHandleDragStart: function (e) {
+ L.DomEvent.stop(e);
+ var handles = e.target.parentNode;
+ var mousePos = L.DomEvent.getMousePosition(e.pointers ? e.srcEvent : e, handles);
+ e.target.isDragged = true;
+ e.target.dragStartPos = mousePos;
+
+ if (!handles.lastDraggedHandle)
+ handles.lastDraggedHandle = 'end';
+ var swap = handles.lastDraggedHandle !== e.target.type;
+ if (swap) {
+ handles.lastDraggedHandle = e.target.type;
+ var pos = e.target.pos;
+ this._map._socket.sendMessage('windowselecttext id=' + e.target.dialogId +
+ ' swap=true x=' + pos.x + ' y=' + pos.y);
+ }
+ },
+
+ _onSelectionHandleDrag: function (e) {
+ var handles = this._calcInputBar.textSelection.handles;
+ var startHandle = handles.children[0];
+ var endHandle = handles.children[1];
+ if (!endHandle || !startHandle)
+ return;
+
+ var draggedHandle;
+ if (startHandle.isDragged)
+ draggedHandle = startHandle;
+ else if (endHandle.isDragged)
+ draggedHandle = endHandle;
+ if (!draggedHandle)
+ return;
+
+ var dragEnd = e.type === 'mouseup' || e.type === 'mouseout' || e.type === 'panend';
+ if (dragEnd)
+ draggedHandle.isDragged = false;
+ var mousePos = L.DomEvent.getMousePosition(e.pointers ? e.srcEvent : e, handles);
+ var pos = draggedHandle.pos.add(mousePos.subtract(draggedHandle.dragStartPos));
+ var maxX = parseInt(handles.style.width) - 5;
+ var maxY = parseInt(handles.style.height) - 5;
+ if (pos.x < handles.offsetX)
+ pos.x = dragEnd ? draggedHandle.lastPos.x : handles.offsetX;
+ else if (mousePos.x > maxX)
+ pos.x = dragEnd ? draggedHandle.lastPos.x : maxX;
+ if (pos.y < handles.offsetY)
+ pos.y = dragEnd ? draggedHandle.lastPos.y : handles.offsetY;
+ else if (mousePos.y > maxY)
+ pos.y = dragEnd ? draggedHandle.lastPos.y : maxY;
+
+ if (Math.abs(pos.y - draggedHandle.lastPos.y) < 6) {
+ pos.y = draggedHandle.lastPos.y;
+ }
+
+ if (draggedHandle.type === 'end') {
+ if (startHandle.pos.y - pos.y > 2)
+ pos.y = draggedHandle.lastPos.y;
+ if (startHandle.pos.y - pos.y > -2 && pos.x - startHandle.pos.x < 2)
+ pos = draggedHandle.lastPos;
+ }
+ if (draggedHandle.type === 'start') {
+ if (pos.y - endHandle.pos.y > 2)
+ pos.y = draggedHandle.lastPos.y;
+ if (pos.y - endHandle.pos.y > -endHandle.rowHeight && endHandle.pos.x - pos.x < 2)
+ pos = draggedHandle.lastPos;
+ }
+
+ var handlePos = pos;
+ if (dragEnd) {
+ handlePos = draggedHandle.lastPos;
+ draggedHandle.pos = pos;
+ }
+
+ L.DomUtil.setStyle(draggedHandle, 'left', handlePos.x + 'px');
+ L.DomUtil.setStyle(draggedHandle, 'top', handlePos.y + 'px');
+ this._map._socket.sendMessage('windowselecttext id=' + draggedHandle.dialogId +
+ ' swap=false x=' + pos.x + ' y=' + pos.y);
},
focus: function(dlgId, acceptInput) {
@@ -615,6 +748,24 @@ L.Control.LokDialog = L.Control.extend({
var textSelectionLayer = L.DomUtil.create('div', 'inputbar_selection_layer', container);
var selections = L.DomUtil.create('div', 'inputbar_selections', textSelectionLayer);
+ // create text selection handles
+ var handles = L.DomUtil.create('div', 'inputbar_selection_handles', textSelectionLayer);
+ L.DomUtil.setStyle(handles, 'position', 'absolute');
+ L.DomUtil.setStyle(handles, 'background', 'transparent');
+ this._setCanvasWidthHeight(handles, width, height);
+ handles.offsetX = 48;
+ handles.offsetY = 0;
+ var startHandle = document.createElement('div');
+ L.DomUtil.addClass(startHandle, 'leaflet-selection-marker-start');
+ startHandle.dialogId = id;
+ startHandle.type = 'start';
+ L.DomEvent.on(startHandle, 'mousedown', this._onSelectionHandleDragStart, this);
+ var endHandle = document.createElement('div');
+ L.DomUtil.addClass(endHandle, 'leaflet-selection-marker-end');
+ endHandle.dialogId = id;
+ endHandle.type = 'end';
+ L.DomEvent.on(endHandle, 'mousedown', this._onSelectionHandleDragStart, this);
+
// Don't show the inputbar until we get the contents.
$(container).parent().hide();
@@ -629,14 +780,15 @@ L.Control.LokDialog = L.Control.extend({
width: width,
height: height,
cursor: null,
- textSelection: {rectangles: selections},
+ textSelection: {rectangles: selections, handles: handles, startHandle: startHandle, endHandle: endHandle},
child: null, // never used for inputbar
title: null // never used for inputbar
};
this._createDialogCursor(strId);
- this._postLaunch(id, container, canvas);
+ this._postLaunch(id, container, handles);
+ this._setupCalcInputBarGestures(id, handles, startHandle, endHandle);
this._calcInputBar = this._dialogs[id];
console.log('_launchCalcInputBar: end');
@@ -759,16 +911,17 @@ L.Control.LokDialog = L.Control.extend({
},
_postLaunch: function(id, panelContainer, panelCanvas) {
-
- this._setupWindowEvents(id, panelCanvas/*, dlgInput*/);
-
- L.DomEvent.on(panelContainer, 'mouseleave', function() {
- // Move the mouse off-screen when we leave the sidebar
- // so we don't leave edge-elements highlighted as if
- // the mouse is still over them.
- this._map.lastActiveTime = Date.now();
- this._postWindowMouseEvent('move', id, -1, -1, 1, 0, 0);
- }, this);
+ if (window.mode.isDesktop()) {
+ this._setupWindowEvents(id, panelCanvas/*, dlgInput*/);
+
+ L.DomEvent.on(panelContainer, 'mouseleave', function () {
+ // Move the mouse off-screen when we leave the sidebar
+ // so we don't leave edge-elements highlighted as if
+ // the mouse is still over them.
+ this._map.lastActiveTime = Date.now();
+ this._postWindowMouseEvent('move', id, -1, -1, 1, 0, 0);
+ }, this);
+ }
// Render window.
this._sendPaintWindowRect(id);
@@ -777,13 +930,32 @@ L.Control.LokDialog = L.Control.extend({
_setupWindowEvents: function(id, canvas/*, dlgInput*/) {
L.DomEvent.on(canvas, 'contextmenu', L.DomEvent.preventDefault);
L.DomEvent.on(canvas, 'mousemove', function(e) {
- this._postWindowMouseEvent('move', id, e.offsetX, e.offsetY, 1, 0, 0);
+ if (this._isSelectionHandleDragged()) {
+ this._onSelectionHandleDrag(e);
+ return;
+ }
+ var pos = this._isSelectionHandle(e.target) ? L.DomEvent.getMousePosition(e, canvas) : {x: e.offsetX, y: e.offsetY};
+ this._postWindowMouseEvent('move', id, pos.x, pos.y, 1, 0, 0);
// Keep map active while user is playing with sidebar/dialog.
this._map.lastActiveTime = Date.now();
}, this);
+ L.DomEvent.on(canvas, 'mouseleave', function(e) {
+ if (this._isSelectionHandleDragged()) {
+ this._onSelectionHandleDrag(e);
+ }
+ }, this);
+
L.DomEvent.on(canvas, 'mousedown mouseup', function(e) {
L.DomEvent.stop(e);
+ if (this._isSelectionHandleDragged() && e.type === 'mouseup') {
+ this._onSelectionHandleDrag(e);
+ return;
+ }
+
+ if (canvas.lastDraggedHandle)
+ canvas.lastDraggedHandle = null;
+
var buttons = 0;
if (this._map['mouse']) {
buttons |= e.button === this._map['mouse'].JSButtons.left ? this._map['mouse'].LOButtons.left : 0;
@@ -792,9 +964,18 @@ L.Control.LokDialog = L.Control.extend({
} else {
buttons = 1;
}
+
+ var modifier = 0;
+ var shift = e.shiftKey ? this._map.keyboard.keyModifier.shift : 0;
+ var ctrl = e.ctrlKey ? this._map.keyboard.keyModifier.ctrl : 0;
+ var alt = e.altKey ? this._map.keyboard.keyModifier.alt : 0;
+ var cmd = e.metaKey ? this._map.keyboard.keyModifier.ctrlMac : 0;
+ modifier = shift | ctrl | alt | cmd;
+
// 'mousedown' -> 'buttondown'
var lokEventType = e.type.replace('mouse', 'button');
- this._postWindowMouseEvent(lokEventType, id, e.offsetX, e.offsetY, 1, buttons, 0);
+ var pos = this._isSelectionHandle(e.target) ? L.DomEvent.getMousePosition(e, canvas) : {x: e.offsetX, y: e.offsetY};
+ this._postWindowMouseEvent(lokEventType, id, pos.x, pos.y, 1, buttons, modifier);
this._map.setWinId(id);
//dlgInput.focus();
}, this);
@@ -806,6 +987,48 @@ L.Control.LokDialog = L.Control.extend({
});
},
+ _setupCalcInputBarGestures: function(id, canvas, startHandle, endHandle) {
+ if (window.mode.isDesktop())
+ return;
+
+ var hammerContent = new Hammer.Manager(canvas, {});
+ var that = this;
+ var singleTap = new Hammer.Tap({event: 'singletap' });
+ var doubleTap = new Hammer.Tap({event: 'doubletap', taps: 2 });
+ var pan = new Hammer.Pan({event: 'pan' });
+ hammerContent.add([doubleTap, singleTap, pan]);
+ singleTap.requireFailure(doubleTap);
+
+
+ hammerContent.on('singletap doubletap', function(ev) {
+ var handles = that._calcInputBar.textSelection.handles;
+ handles.lastDraggedHandle = null;
+ var startHandle = handles.children[0];
+ var endHandle = handles.children[1];
+ if (startHandle)
+ startHandle.isDragged = false;
+ if (endHandle)
+ endHandle.isDragged = false;
+
+ var point = L.DomEvent.getMousePosition(ev.srcEvent, handles);
+
+ that._postWindowMouseEvent('buttondown', id, point.x, point.y, 1, 1, 0);
+ that._postWindowMouseEvent('buttonup', id, point.x, point.y, 1, 1, 0);
+ if (ev.type === 'doubletap') {
+ that._postWindowMouseEvent('buttondown', id, point.x, point.y, 1, 1, 0);
+ that._postWindowMouseEvent('buttonup', id, point.x, point.y, 1, 1, 0);
+ }
+ });
+
+ hammerContent.on('panmove panend', L.bind(this._onSelectionHandleDrag, this));
+
+ var hammerEndHandle = new Hammer.Manager(endHandle, {recognizers:[[Hammer.Pan]]});
+ hammerEndHandle.on('panstart', L.bind(this._onSelectionHandleDragStart, this));
+
+ var hammerStartHandle = new Hammer.Manager(startHandle, {recognizers:[[Hammer.Pan]]});
+ hammerStartHandle.on('panstart', L.bind(this._onSelectionHandleDragStart, this));
+ },
+
_setupGestures: function(dialogContainer, id, canvas) {
var targetId = toZoomTargetId(canvas.id);
var zoomTarget = $('#' + targetId).parent().get(0);
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 60ed55c6f..1442b4a23 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -417,6 +417,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
tokens[0] != "savetostorage" &&
tokens[0] != "selectgraphic" &&
tokens[0] != "selecttext" &&
+ tokens[0] != "windowselecttext" &&
tokens[0] != "setclientpart" &&
tokens[0] != "selectclientpart" &&
tokens[0] != "moveselectedclientparts" &&
More information about the Libreoffice-commits
mailing list