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

Reply via email to