bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h | 23 +- kit/ChildSession.cpp | 6 kit/KitHelper.hpp | 4 loleaflet/css/spreadsheet.css | 20 + loleaflet/images/cell-autofill-marker.svg | 3 loleaflet/images/cell-resize-marker.svg | 3 loleaflet/src/layer/tile/TileLayer.js | 217 +++++++++++++++++++ tools/KitClient.cpp | 2 8 files changed, 275 insertions(+), 3 deletions(-)
New commits: commit 42c4c864feaa3559c1384b64a893fa6d24a123ac Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Sun Jul 28 10:45:41 2019 +0900 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Mon Jul 29 08:51:55 2019 +0200 tdf#125425 add cell selection markers and cell auto fill marker This replaces the standard text selection marker with a cell selection markers to resize the cell selection. This looks and behaves better for cells in Calc and it is also easier to deal with. Additionally add auto fill marker to perform the auto fill action on the cells. This was not possible to do previously unless you hit an invisible part of the cell selection, which is not convenient when using touch. Change-Id: Ia02d03b7b8e8d98412ea98eb92fb47d1505ef979 Reviewed-on: https://gerrit.libreoffice.org/76494 Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> Tested-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h index f3fc91b2f..4277adbe6 100644 --- a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h +++ b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h @@ -164,7 +164,7 @@ typedef enum /** * The size and/or the position of the graphic selection changed, * the rotation angle of the embedded graphic object, and a property list - * which can be used for informing the client about severl properties. + * which can be used for informing the client about several properties. * * Format is "x, y, width, height, angle, { list of properties }", * where angle is in 100th of degree, and the property list is optional. @@ -195,7 +195,7 @@ typedef enum * } * * where the "svg" property is a string containing an svg document - * which is a rapresentation of the pie segment. + * which is a representation of the pie segment. */ LOK_CALLBACK_GRAPHIC_SELECTION = 6, @@ -593,6 +593,7 @@ typedef enum * "type" tells the type of the window the action is associated with * - "dialog" - window is a dialog * - "child" - window is a floating window (combo boxes, etc.) + * - "deck" - window is a docked/floating deck (i.e. the sidebar) * * "action" can take following values: * - "created" - window is created in the backend, client can render it now @@ -647,7 +648,23 @@ typedef enum * Profiling tracing information single string of multiple lines * containing <pid> <timestamp> and zone start/stop information */ - LOK_CALLBACK_PROFILE_FRAME = 41 + LOK_CALLBACK_PROFILE_FRAME = 41, + + /** + * The position and size of the cell selection area. It is used to + * draw the selection handles for cells in Calc documents. + * + * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES. + */ + LOK_CALLBACK_CELL_SELECTION_AREA = 42, + + /** + * The position and size of the cell auto fill area. It is used to + * trigger auto fill functionality if that area is hit. + * + * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES. + */ + LOK_CALLBACK_CELL_AUTO_FILL_AREA = 43, } LibreOfficeKitCallbackType; diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp index b3ee31b0a..01853e94b 100644 --- a/kit/ChildSession.cpp +++ b/kit/ChildSession.cpp @@ -2133,6 +2133,12 @@ void ChildSession::loKitCallback(const int type, const std::string& payload) case LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY: // these are not handled here. break; + case LOK_CALLBACK_CELL_SELECTION_AREA: + sendTextFrame("cellselectionarea: " + payload); + break; + case LOK_CALLBACK_CELL_AUTO_FILL_AREA: + sendTextFrame("cellautofillarea: " + payload); + break; #if !ENABLE_DEBUG // we want a compilation-time failure in the debug builds; but ERR in the diff --git a/kit/KitHelper.hpp b/kit/KitHelper.hpp index 88d090428..9f51fe528 100644 --- a/kit/KitHelper.hpp +++ b/kit/KitHelper.hpp @@ -133,6 +133,10 @@ namespace LOKitHelper return "SIGNATURE_STATUS"; case LOK_CALLBACK_PROFILE_FRAME: return "PROFILE_FRAME"; + case LOK_CALLBACK_CELL_SELECTION_AREA: + return "CELL_SELECTION_AREA"; + case LOK_CALLBACK_CELL_AUTO_FILL_AREA: + return "CELL_AUTO_FILL_AREA"; } assert(!"Missing LOK_CALLBACK type"); diff --git a/loleaflet/css/spreadsheet.css b/loleaflet/css/spreadsheet.css index 4411b69ab..a0059b07f 100644 --- a/loleaflet/css/spreadsheet.css +++ b/loleaflet/css/spreadsheet.css @@ -202,6 +202,26 @@ cursor: row-resize; } +.spreadsheet-cell-resize-marker { + margin-left: 0px; + margin-top: 0px; + width: 16px; + height: 16px; + background-image: url('images/cell-resize-marker.svg'); + background-size: 100% 100%; + background-repeat: no-repeat; +} + +.spreadsheet-cell-autofill-marker { + margin-left: 0px; + margin-top: 0px; + width: 16px; + height: 16px; + background-image: url('images/cell-autofill-marker.svg'); + background-size: 100% 100%; + background-repeat: no-repeat; +} + .spreadsheet-drop-down-marker { margin-left: 0px; margin-top: 0px; diff --git a/loleaflet/images/cell-autofill-marker.svg b/loleaflet/images/cell-autofill-marker.svg new file mode 100644 index 000000000..921b2e093 --- /dev/null +++ b/loleaflet/images/cell-autofill-marker.svg @@ -0,0 +1,3 @@ +<svg version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> + <rect x="1.8212" y="1.8212" width="28.358" height="28.358" fill-opacity=".86275" stroke="#000" stroke-linecap="round" stroke-width="1.6425"/> +</svg> diff --git a/loleaflet/images/cell-resize-marker.svg b/loleaflet/images/cell-resize-marker.svg new file mode 100644 index 000000000..1fcc3b9e7 --- /dev/null +++ b/loleaflet/images/cell-resize-marker.svg @@ -0,0 +1,3 @@ +<svg version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> + <ellipse cx="16" cy="16" rx="15.284" ry="15.284" fill="#fff" fill-opacity=".86275" stroke="#000" stroke-linecap="round" stroke-width="1.4328"/> +</svg> diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index 9cd0935bd..d7f74a773 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -149,6 +149,30 @@ L.TileLayer = L.GridLayer.extend({ interactive: false }); + this._cellResizeMarkerStart = L.marker(new L.LatLng(0, 0), { + icon: L.divIcon({ + className: 'spreadsheet-cell-resize-marker', + iconSize: null + }), + draggable: true + }); + + this._cellResizeMarkerEnd = L.marker(new L.LatLng(0, 0), { + icon: L.divIcon({ + className: 'spreadsheet-cell-resize-marker', + iconSize: null + }), + draggable: true + }); + + this._cellAutofillMarker = L.marker(new L.LatLng(0, 0), { + icon: L.divIcon({ + className: 'spreadsheet-cell-autofill-marker', + iconSize: null + }), + draggable: true + }); + this._emptyTilesCount = 0; this._msgQueue = []; this._toolbarCommandValues = {}; @@ -287,6 +311,10 @@ L.TileLayer = L.GridLayer.extend({ this._selectionHandles[key].on('drag dragend', this._onSelectionHandleDrag, this); } + this._cellResizeMarkerStart.on('dragstart drag dragend', this._onCellResizeMarkerDrag, this); + this._cellResizeMarkerEnd.on('dragstart drag dragend', this._onCellResizeMarkerDrag, this); + this._cellAutofillMarker.on('dragstart drag dragend', this._onCellResizeMarkerDrag, this); + map.setPermission(this.options.permission); map.fire('statusindicator', {statusType: 'loleafletloaded'}); @@ -451,6 +479,12 @@ L.TileLayer = L.GridLayer.extend({ else if (textMsg.startsWith('textselectionstart:')) { this._onTextSelectionStartMsg(textMsg); } + else if (textMsg.startsWith('cellselectionarea:')) { + this._onCellSelectionAreaMsg(textMsg); + } + else if (textMsg.startsWith('cellautofillarea:')) { + this._onCellAutoFillAreaMsg(textMsg); + } else if (textMsg.startsWith('windowpaint:')) { this._onDialogPaintMsg(textMsg, img); } @@ -1343,6 +1377,37 @@ L.TileLayer = L.GridLayer.extend({ } }, + _onCellSelectionAreaMsg: function (textMsg) { + var strTwips = textMsg.match(/\d+/g); + if (strTwips != null && this._map._permission === 'edit') { + var topLeftTwips = new L.Point(parseInt(strTwips[0]), parseInt(strTwips[1])); + var offset = new L.Point(parseInt(strTwips[2]), parseInt(strTwips[3])); + var bottomRightTwips = topLeftTwips.add(offset); + var oldSelection = this._cellSelectionArea; + this._cellSelectionArea = new L.LatLngBounds( + this._twipsToLatLng(topLeftTwips, this._map.getZoom()), + this._twipsToLatLng(bottomRightTwips, this._map.getZoom())); + + this._updateScrollOnCellSelection(oldSelection, this._cellSelectionArea); + } else { + this._cellSelectionArea = null; + } + }, + + _onCellAutoFillAreaMsg: function (textMsg) { + var strTwips = textMsg.match(/\d+/g); + if (strTwips != null && this._map._permission === 'edit') { + var topLeftTwips = new L.Point(parseInt(strTwips[0]), parseInt(strTwips[1])); + var offset = new L.Point(parseInt(strTwips[2]), parseInt(strTwips[3])); + var bottomRightTwips = topLeftTwips.add(offset); + this._cellAutoFillArea = new L.LatLngBounds( + this._twipsToLatLng(topLeftTwips, this._map.getZoom()), + this._twipsToLatLng(bottomRightTwips, this._map.getZoom())); + } else { + this._cellAutoFillArea = null; + } + }, + _onDialogPaintMsg: function(textMsg, img) { var command = this._map._socket.parseServerCmd(textMsg); @@ -2158,6 +2223,110 @@ L.TileLayer = L.GridLayer.extend({ } }, + // Update dragged text selection. + _onCellResizeMarkerDrag: function (e) { + var buttonType = null; + if (e.type === 'dragstart') { + e.target.isDragged = true; + + // handle scrolling + if (this._cellAutofillMarker === e.target) { + var autoFillPosition = this._latLngToTwips(this._cellAutoFillArea.getCenter()); + this._postMouseEvent('buttondown', autoFillPosition.x, autoFillPosition.y, 1, 1, 0); + buttonType = 'move'; + } + } + else if (e.type === 'drag') { + var event = e.originalEvent; + if (e.originalEvent.touches && e.originalEvent.touches.length > 0) { + event = e.originalEvent.touches[0]; + } + if (!event.pageX && !event.pageY) { + return; + } + + // handle scroling + + // This is rather hacky, but it seems to be the only way to make the + // marker follow the mouse cursor if the document is autoscrolled under + // us. (This can happen when we're changing the selection if the cursor + // moves somewhere that is considered off screen.) + + // Onscreen position of the cursor, i.e. relative to the browser window + var boundingrect = e.target._icon.getBoundingClientRect(); + var cursorPos = L.point(boundingrect.left, boundingrect.top); + var expectedPos = L.point(event.pageX, event.pageY).subtract(e.target.dragging._draggable.startOffset); + + // Dragging the selection handles vertically more than one line on a touch + // device is more or less impossible without this hack. + if (!(typeof e.originalEvent.type === 'string' && e.originalEvent.type === 'touchmove')) { + // If the map has been scrolled, but the cursor hasn't been updated yet, then + // the current mouse position differs. + if (!expectedPos.equals(cursorPos)) { + var correction = expectedPos.subtract(cursorPos); + + e.target.dragging._draggable._startPoint = e.target.dragging._draggable._startPoint.add(correction); + e.target.dragging._draggable._startPos = e.target.dragging._draggable._startPos.add(correction); + e.target.dragging._draggable._newPos = e.target.dragging._draggable._newPos.add(correction); + + e.target.dragging._draggable._updatePosition(); + } + } + var containerPos = new L.Point(expectedPos.x - this._map._container.getBoundingClientRect().left, + expectedPos.y - this._map._container.getBoundingClientRect().top); + + containerPos = containerPos.add(e.target.dragging._draggable.startOffset); + this._map.fire('handleautoscroll', {pos: containerPos, map: this._map}); + + // cell auto marker + if (this._cellAutofillMarker === e.target) { + buttonType = 'move'; + } + } else if (e.type === 'dragend') { + e.target.isDragged = false; + + // handle scrolling + this._map.focus(); + this._map.fire('scrollvelocity', {vx: 0, vy: 0}); + + // cell auto marker + if (this._cellAutofillMarker === e.target) { + buttonType = 'buttonup'; + } + } + + // modify the mouse position - move to center of the marker + var aMousePosition = e.target.getLatLng(); + aMousePosition = this._map.project(aMousePosition); + var size; + if (this._cellResizeMarkerStart === e.target) { + size = this._cellResizeMarkerStart._icon.getBoundingClientRect(); + } + else if (this._cellResizeMarkerEnd === e.target) { + size = this._cellResizeMarkerEnd._icon.getBoundingClientRect(); + } + else if (this._cellAutofillMarker === e.target) { + size = this._cellAutofillMarker._icon.getBoundingClientRect(); + } + aMousePosition = aMousePosition.add(new L.Point(size.width / 2, size.height / 2)); + aMousePosition = this._map.unproject(aMousePosition); + aMousePosition = this._latLngToTwips(aMousePosition); + + if (this._cellResizeMarkerStart === e.target) { + this._postSelectTextEvent('start', aMousePosition.x, aMousePosition.y); + if (e.type === 'dragend') + this._onUpdateCellResizeMarkers(); + } + else if (this._cellResizeMarkerEnd === e.target) { + this._postSelectTextEvent('end', aMousePosition.x, aMousePosition.y); + if (e.type === 'dragend') + this._onUpdateCellResizeMarkers(); + } + else if (this._cellAutofillMarker === e.target) { + this._postMouseEvent(buttonType, aMousePosition.x, aMousePosition.y, 1, 1, 0); + } + }, + // Update group layer selection handler. _onUpdateGraphicSelection: function () { if (this._graphicSelection && !this._isEmptyRectangle(this._graphicSelection)) { @@ -2219,6 +2388,7 @@ L.TileLayer = L.GridLayer.extend({ }, _onUpdateCellCursor: function (horizontalDirection, verticalDirection, onPgUpDn) { + this._onUpdateCellResizeMarkers(); if (this._cellCursor && !this._isEmptyRectangle(this._cellCursor)) { var mapBounds = this._map.getBounds(); if (!mapBounds.contains(this._cellCursor) && !this._cellCursorXY.equals(this._prevCellCursorXY)) { @@ -2341,8 +2511,55 @@ L.TileLayer = L.GridLayer.extend({ return this._dropDownButton; }, + _onUpdateCellResizeMarkers: function () { + if (this._selections.getLayers().length !== 0 || (this._cellCursor && !this._isEmptyRectangle(this._cellCursor))) { + if (this._isEmptyRectangle(this._cellSelectionArea) && this._isEmptyRectangle(this._cellCursor)) { + return; + } + + var cellRectangle = this._cellSelectionArea ? this._cellSelectionArea : this._cellCursor; + + if (!this._cellResizeMarkerStart.isDragged) { + this._map.addLayer(this._cellResizeMarkerStart); + var posStart = this._map.project(cellRectangle.getNorthWest()); + var sizeStart = this._cellResizeMarkerStart._icon.getBoundingClientRect(); + posStart = posStart.subtract(new L.Point(sizeStart.width / 2, sizeStart.height / 2)); + posStart = this._map.unproject(posStart); + this._cellResizeMarkerStart.setLatLng(posStart); + } + if (!this._cellResizeMarkerEnd.isDragged) { + this._map.addLayer(this._cellResizeMarkerEnd); + var posEnd = this._map.project(cellRectangle.getSouthEast()); + var sizeEnd = this._cellResizeMarkerEnd._icon.getBoundingClientRect(); + posEnd = posEnd.subtract(new L.Point(sizeEnd.width / 2, sizeEnd.height / 2)); + posEnd = this._map.unproject(posEnd); + this._cellResizeMarkerEnd.setLatLng(posEnd); + } + if (!this._cellAutofillMarker.isDragged) { + this._map.addLayer(this._cellAutofillMarker); + var cellAutoFillMarkerPoisition = cellRectangle.getCenter(); + cellAutoFillMarkerPoisition.lat = cellRectangle.getSouth(); + cellAutoFillMarkerPoisition = this._map.project(cellAutoFillMarkerPoisition); + var sizeAutoFill = this._cellAutofillMarker._icon.getBoundingClientRect(); + cellAutoFillMarkerPoisition = cellAutoFillMarkerPoisition.subtract(new L.Point(sizeAutoFill.width / 2, sizeAutoFill.height / 2)); + cellAutoFillMarkerPoisition = this._map.unproject(cellAutoFillMarkerPoisition); + this._cellAutofillMarker.setLatLng(cellAutoFillMarkerPoisition); + } + } + else { + this._map.removeLayer(this._cellResizeMarkerStart); + this._map.removeLayer(this._cellResizeMarkerEnd); + this._map.removeLayer(this._cellAutofillMarker); + } + }, + // Update text selection handlers. _onUpdateTextSelection: function () { + if (this._docType === 'spreadsheet') { + this._onUpdateCellResizeMarkers(); + return; + } + var startMarker, endMarker; for (var key in this._selectionHandles) { if (key === 'start') { diff --git a/tools/KitClient.cpp b/tools/KitClient.cpp index bbf9f089b..c68de9adf 100644 --- a/tools/KitClient.cpp +++ b/tools/KitClient.cpp @@ -85,6 +85,8 @@ extern "C" CASE(CONTEXT_CHANGED); CASE(SIGNATURE_STATUS); CASE(PROFILE_FRAME); + CASE(CELL_SELECTION_AREA); + CASE(CELL_AUTO_FILL_AREA); #undef CASE } std::cout << " payload: " << payload << std::endl; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits