From 15bf7877718d6293035db67696668bb422f289aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6kay=20=C5=9Eat=C4=B1r?= Date: Sun, 17 Jan 2021 18:31:17 +0300 Subject: [PATCH] CanvasSectionContainer: Fixes. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugs related to ordering of sections are fixed. addSection and createSection functions are separated. contextMenu event is added. Sections' drawing orders are updated for future uses. Signed-off-by: Gökay Şatır Change-Id: I89127c97ad7b7dac4b293c4108c44cb6c4427134 --- .../src/layer/tile/CanvasSectionContainer.ts | 147 ++++++++++-------- loleaflet/src/layer/tile/CanvasTileLayer.js | 21 +-- 2 files changed, 98 insertions(+), 70 deletions(-) diff --git a/loleaflet/src/layer/tile/CanvasSectionContainer.ts b/loleaflet/src/layer/tile/CanvasSectionContainer.ts index 434f23a54..4c008996a 100644 --- a/loleaflet/src/layer/tile/CanvasSectionContainer.ts +++ b/loleaflet/src/layer/tile/CanvasSectionContainer.ts @@ -102,6 +102,7 @@ class CanvasSectionObject { onMouseLeave: Function; // Parameters: Point [x, y], e (native event object) onClick: Function; // Parameters: Point [x, y], e (native event object) onDoubleClick: Function; // Parameters: Point [x, y], e (native event object) + onContextMenu: Function; onMouseWheel: Function; // Parameters: Point [x, y], DeltaY, e (native event object) onLongPress: Function; // Parameters: Point [x, y], e (native event object) onMultiTouchStart: Function; // Parameters: e (native event object) @@ -130,6 +131,7 @@ class CanvasSectionObject { this.onMouseLeave = options.onMouseLeave ? options.onMouseLeave: function() {}; this.onClick = options.onClick ? options.onClick: function() {}; this.onDoubleClick = options.onDoubleClick ? options.onDoubleClick: function() {}; + this.onContextMenu = options.onContextMenu ? options.onContextMenu: function() {}; this.onMouseWheel = options.onMouseWheel ? options.onMouseWheel: function() {}; this.onLongPress = options.onLongPress ? options.onLongPress: function() {}; this.onMultiTouchStart = options.onMultiTouchStart ? options.onMultiTouchStart: function() {}; @@ -179,6 +181,7 @@ class CanvasSectionContainer { this.canvas.onmouseup = this.onMouseUp.bind(this); this.canvas.onclick = this.onClick.bind(this); this.canvas.ondblclick = this.onDoubleClick.bind(this); + this.canvas.oncontextmenu = this.onContextMenu.bind(this); this.canvas.onwheel = this.onMouseWheel.bind(this); this.canvas.onmouseleave = this.onMouseLeave.bind(this); this.canvas.ontouchstart = this.onTouchStart.bind(this); @@ -343,12 +346,11 @@ class CanvasSectionContainer { if (section) { section.onLongPress(this.convertPositionToSectionLocale(section, this.mousePosition), e); } - this.potentialLongPress = false; } } private onMouseDown (e: MouseEvent) { // Ignore this event, just rely on this.draggingSomething variable. - if (!this.touchEventInProgress) { + if (e.button === 0 && !this.touchEventInProgress) { // So, we only handle left button. this.clearMousePositions(); this.positionOnMouseDown = this.convertPositionToCanvasLocale(e); @@ -361,7 +363,7 @@ class CanvasSectionContainer { } private onMouseUp (e: MouseEvent) { // Should be ignored unless this.draggingSomething = true. - if (!this.touchEventInProgress) { + if (e.button === 0 && !this.touchEventInProgress) { this.positionOnMouseUp = this.convertPositionToCanvasLocale(e); if (!this.draggingSomething) { @@ -379,6 +381,22 @@ class CanvasSectionContainer { } } + private onContextMenu (e: MouseEvent) { + var mousePosition = this.convertPositionToCanvasLocale(e); + var section: CanvasSectionObject = this.findSectionContainingPoint(mousePosition); + if (section) { + section.onContextMenu(); + } + if (this.potentialLongPress) { + // LongPress triggers context menu. + // We should stop propagating here because we are using different context menu handlers for touch and mouse events. + // By stopping this event here, we can have real context menus (for mice) and other handlers (for longpress) at the same time (see Control.RowHeader.js). + e.preventDefault(); + e.stopPropagation(); + return false; + } + } + private onMouseWheel (e: WheelEvent) { var point = this.convertPositionToCanvasLocale(e); var delta = e.deltaY; @@ -478,6 +496,14 @@ class CanvasSectionContainer { } onResize (newWidth: number, newHeight: number) { + var container: HTMLElement = this.canvas.parentNode; + var cRect: ClientRect = container.getBoundingClientRect(); + if (!newWidth) + newWidth = cRect.right - cRect.left; + + if (!newHeight) + newHeight = cRect.bottom - cRect.top; + this.dpiScale = window.devicePixelRatio; newWidth = Math.floor(newWidth * this.dpiScale); newHeight = Math.floor(newHeight * this.dpiScale); @@ -504,7 +530,7 @@ class CanvasSectionContainer { findSectionContainingPoint (point: Array): any { for (var i: number = this.sections.length - 1; i > -1; i--) { // Search from top to bottom. Top section will be sent as target section. - if (this.sections[i].interactable && this.doesSectionIncludePoint(this.sections[i], point)) + if (this.sections[i].isLocated && this.sections[i].interactable && this.doesSectionIncludePoint(this.sections[i], point)) return this.sections[i]; } @@ -545,10 +571,7 @@ class CanvasSectionContainer { private hitLeft (section: CanvasSectionObject): number { var maxX = -1; for (var i: number = 0; i < this.sections.length; i++) { - if (!this.sections[i].isLocated) - continue; - - if (this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { + if (this.sections[i].isLocated && this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { var currentLeft = this.sections[i].myTopLeft[0] + this.sections[i].size[0]; if (currentLeft > maxX && currentLeft < section.myTopLeft[0]) { if (this.doSectionsIntersectOnYAxis(this.sections[i], section)) { @@ -567,10 +590,7 @@ class CanvasSectionContainer { private hitRight (section: CanvasSectionObject): number { var minX = Infinity; for (var i: number = 0; i < this.sections.length; i++) { - if (!this.sections[i].isLocated) - continue; - - if (this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { + if (this.sections[i].isLocated && this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { var currentRight = this.sections[i].myTopLeft[0]; if (currentRight < minX && currentRight > section.myTopLeft[0]) { if (this.doSectionsIntersectOnYAxis(this.sections[i], section)) { @@ -590,10 +610,7 @@ class CanvasSectionContainer { private hitTop (section: CanvasSectionObject): number { var maxY = -1; for (var i: number = 0; i < this.sections.length; i++) { - if (!this.sections[i].isLocated) - continue; - - if (this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { + if (this.sections[i].isLocated && this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { var currentTop = this.sections[i].myTopLeft[1] + this.sections[i].size[1]; if (currentTop > maxY && currentTop < section.myTopLeft[1]) { if (this.doSectionsIntersectOnXAxis(this.sections[i], section)) { @@ -612,10 +629,7 @@ class CanvasSectionContainer { private hitBottom (section: CanvasSectionObject): number { var minY = Infinity; for (var i: number = 0; i < this.sections.length; i++) { - if (!this.sections[i].isLocated) - continue; - - if (this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { + if (this.sections[i].isLocated && this.sections[i].zIndex === section.zIndex && this.sections[i].name !== section.name) { var currentBottom = this.sections[i].myTopLeft[1]; if (currentBottom < minY && currentBottom > section.myTopLeft[1]) { if (this.doSectionsIntersectOnXAxis(this.sections[i], section)) { @@ -644,13 +658,11 @@ class CanvasSectionContainer { private locateSections () { for (var i: number = 0; i < this.sections.length; i++) { var section: CanvasSectionObject = this.sections[i]; - if (section.isLocated) - continue; - + section.isLocated = false; section.myTopLeft = null; var x = section.anchor[1] === 'left' ? section.position[0]: (this.right - (section.position[0] + section.size[0])); var y = section.anchor[0] === 'top' ? section.position[1]: (this.bottom - (section.position[1] + section.size[1])); - if (!section.boundToSection) + if (!section.boundToSection) { section.myTopLeft = [x, y]; if (section.expand[0] !== '') { if (section.expand.includes('left') || section.expand.includes('right')) @@ -658,6 +670,10 @@ class CanvasSectionContainer { if (section.expand.includes('top') || section.expand.includes('bottom')) section.size[1] = 0; } + else { + section.isLocated = true; + } + } else { section.myTopLeft = [0, 0]; section.size = [0, 0]; @@ -667,10 +683,7 @@ class CanvasSectionContainer { // We have initial positions, now we'll expand them. for (var i: number = 0; i < this.sections.length; i++) { var section: CanvasSectionObject = this.sections[i]; - if (section.isLocated) - continue; - - if (section.expand && !section.boundToSection) { + if (section.expand[0] !== '' && !section.boundToSection) { if (section.expand.includes('left')) { var initialX = section.myTopLeft[0]; section.myTopLeft[0] = this.hitLeft(section); @@ -693,16 +706,11 @@ class CanvasSectionContainer { section.isLocated = true; } - else if (!section.boundToSection) - section.isLocated = true; // expand is empty => its location/size is already defined. } // Set location and size of bound sections. for (var i: number = 0; i < this.sections.length; i++) { var section: CanvasSectionObject = this.sections[i]; - if (section.isLocated) - continue; - if (section.boundToSection) { var parentSection = this.getSectionWithName(section.boundToSection); if (parentSection) { @@ -710,9 +718,8 @@ class CanvasSectionContainer { section.size[1] = parentSection.size[1]; section.myTopLeft[0] = parentSection.myTopLeft[0]; section.myTopLeft[1] = parentSection.myTopLeft[1]; + section.isLocated = true; } - - section.isLocated = true; // FIXME: do only if parentSection is available ? } } } @@ -732,13 +739,12 @@ class CanvasSectionContainer { // According to processing order. Section with the highest processing order will be calculated last. for (var i: number = 0; i < this.sections.length - 1; i++) { var zIndex = this.sections[i].zIndex; - while (i < this.sections.length - 1 && zIndex === this.sections[i + 1].zIndex) { - if (this.sections[i].processingOrder > this.sections[i + 1].processingOrder) { - var temp = this.sections[i + 1]; - this.sections[i + 1] = this.sections[i]; + for (var j: number = i + 1; j < this.sections.length && this.sections[j].zIndex === zIndex; j++) { + if (this.sections[i].processingOrder > this.sections[j].processingOrder) { + var temp = this.sections[j]; + this.sections[j] = this.sections[i]; this.sections[i] = temp; } - i++; } } } @@ -747,13 +753,12 @@ class CanvasSectionContainer { // According to drawing order. Section with the highest drawing order will be drawn on top. for (var i: number = 0; i < this.sections.length - 1; i++) { var zIndex = this.sections[i].zIndex; - while (i < this.sections.length - 1 && zIndex === this.sections[i + 1].zIndex) { - if (this.sections[i].drawingOrder > this.sections[i + 1].drawingOrder) { - var temp = this.sections[i + 1]; - this.sections[i + 1] = this.sections[i]; + for (var j: number = i + 1; j < this.sections.length && this.sections[j].zIndex === zIndex; j++) { + if (this.sections[i].drawingOrder > this.sections[j].drawingOrder) { + var temp = this.sections[j]; + this.sections[j] = this.sections[i]; this.sections[i] = temp; } - i++; } } } @@ -789,16 +794,24 @@ class CanvasSectionContainer { } } + setPenPosition (section: CanvasSectionObject) { + this.context.setTransform(1, 0, 0, 1, 0, 0); + this.context.translate(section.myTopLeft[0], section.myTopLeft[1]); + } + private drawSections () { + this.context.setTransform(1, 0, 0, 1, 0, 0); this.context.fillStyle = this.clearColor; this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); this.context.font = String(20 * this.dpiScale) + "px Verdana"; for (var i: number = 0; i < this.sections.length; i++) { - this.context.translate(this.sections[i].myTopLeft[0], this.sections[i].myTopLeft[1]); - this.sections[i].onDraw(); - this.context.translate(-this.sections[i].myTopLeft[0], -this.sections[i].myTopLeft[1]); + if (this.sections[i].isLocated) { + this.context.translate(this.sections[i].myTopLeft[0], this.sections[i].myTopLeft[1]); + this.sections[i].onDraw(); + this.context.translate(-this.sections[i].myTopLeft[0], -this.sections[i].myTopLeft[1]); + } } //this.drawSectionBorders(); } @@ -846,23 +859,12 @@ class CanvasSectionContainer { return true; } - addSection (options: any, parentSectionName: string = null) { + createSection (options: any, parentSectionName: string = null) { if (this.newSectionChecks(options)) { // Every section can draw from Point(0, 0), their drawings will be translated to myTopLeft position. var newSection: CanvasSectionObject = new CanvasSectionObject(options); - - newSection.context = this.context; - newSection.documentTopLeft = this.documentTopLeft; - newSection.containerObject = this; - newSection.dpiScale = this.dpiScale; - newSection.sectionProperties.section = newSection; newSection.boundToSection = parentSectionName; - - this.sections.push(newSection); - newSection.onInitialize(); - this.reNewAllSections(false); - this.drawSections(); - + this.pushSection(newSection); return true; } else { @@ -870,6 +872,29 @@ class CanvasSectionContainer { } } + addSection (newSection: CanvasSectionObject) { + if (this.newSectionChecks(newSection)) { + this.pushSection(newSection); + return true; + } + else { + return false; + } + } + + private pushSection (newSection: CanvasSectionObject) { + // Every section can draw from Point(0, 0), their drawings will be translated to myTopLeft position. + newSection.context = this.context; + newSection.documentTopLeft = this.documentTopLeft; + newSection.containerObject = this; + newSection.dpiScale = this.dpiScale; + newSection.sectionProperties.section = newSection; + this.sections.push(newSection); + newSection.onInitialize(); + this.reNewAllSections(false); + this.drawSections(); + } + removeSection (name: string) { var found: boolean = false; for (var i: number = 0; i < this.sections.length; i++) { diff --git a/loleaflet/src/layer/tile/CanvasTileLayer.js b/loleaflet/src/layer/tile/CanvasTileLayer.js index cede91837..3ca59af5b 100644 --- a/loleaflet/src/layer/tile/CanvasTileLayer.js +++ b/loleaflet/src/layer/tile/CanvasTileLayer.js @@ -202,6 +202,8 @@ L.TileSectionManager = L.Class.extend({ if (!ctx) ctx = this._paintContext(); + this._sectionContainer.setPenPosition(this._tilesSection); + if (ctx.paneBoundsActive === true) this._paintWithPanes(tile, ctx); else @@ -210,16 +212,16 @@ L.TileSectionManager = L.Class.extend({ _addTilesSection: function () { var that = this; - this._sectionContainer.addSection({ + this._sectionContainer.createSection({ name: 'tiles', anchor: 'top left', position: [250 * that._dpiScale, 250 * that._dpiScale], // Set its initial position to somewhere blank. Other sections shouldn't cover this point after initializing. size: [0, 0], // Going to be expanded, no initial width or height is necessary. expand: 'top left bottom right', // Expand to all directions. - processingOrder: 1, + processingOrder: 5, drawingOrder: 5, zIndex: 5, - interactable: true, + interactable: false, sectionProperties: { docLayer: that._layer, tsManager: that @@ -233,13 +235,13 @@ L.TileSectionManager = L.Class.extend({ _addGridSection: function () { var that = this; - this._sectionContainer.addSection({ + this._sectionContainer.createSection({ name: 'calc grid', anchor: 'top left', position: [0, 0], size: [0, 0], expand: '', - processingOrder: 2, // Size and position will be copied, this value is not important. + processingOrder: 5, // Size and position will be copied, this value is not important. drawingOrder: 4, zIndex: 5, // Even if this one is drawn on top, won't be able to catch events. @@ -302,13 +304,13 @@ L.TileSectionManager = L.Class.extend({ // This section is added when debug is enabled. Splits are enabled for only Calc for now. _addSplitsSection: function () { var that = this; - this._sectionContainer.addSection({ + this._sectionContainer.createSection({ name: 'splits', anchor: 'top left', position: [0, 0], size: [0, 0], expand: '', - processingOrder: 3, // Size and position will be copied, this value is not important. + processingOrder: 5, // Size and position will be copied, this value is not important. drawingOrder: 8, // Above tiles section (same zIndex, higher drawing order). zIndex: 5, // Even if this one is drawn on top, won't be able to catch events. @@ -324,13 +326,13 @@ L.TileSectionManager = L.Class.extend({ // This section is added when debug is enabled. _addTilePixelGridSection: function () { var that = this; - this._sectionContainer.addSection({ + this._sectionContainer.createSection({ name: 'tile pixel grid', anchor: 'top left', position: [0, 0], size: [0, 0], expand: '', - processingOrder: 4, // Size and position will be copied, this value is not important. + processingOrder: 5, // Size and position will be copied, this value is not important. drawingOrder: 6, zIndex: 5, interactable: false, @@ -572,6 +574,7 @@ L.CanvasTileLayer = L.TileLayer.extend({ if (this._docType === 'spreadsheet') { this._painter._addGridSection(); } + this._syncTileContainerSize(); }, _syncTilePanePos: function () {