diff --git a/core/bubble.js b/core/bubble.js index 51ce808ce..c994db96b 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -21,6 +21,7 @@ goog.require('Blockly.utils.math'); goog.require('Blockly.utils.userAgent'); goog.require('Blockly.Workspace'); +goog.requireType('Blockly.IBubble'); goog.requireType('Blockly.utils.Metrics'); @@ -34,10 +35,11 @@ goog.requireType('Blockly.utils.Metrics'); * anchor point. * @param {?number} bubbleWidth Width of bubble, or null if not resizable. * @param {?number} bubbleHeight Height of bubble, or null if not resizable. + * @implements {Blockly.IBubble} * @constructor */ -Blockly.Bubble = function(workspace, content, shape, anchorXY, - bubbleWidth, bubbleHeight) { +Blockly.Bubble = function( + workspace, content, shape, anchorXY, bubbleWidth, bubbleHeight) { this.workspace_ = workspace; this.content_ = content; this.shape_ = shape; @@ -236,23 +238,21 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { */ this.bubbleGroup_ = Blockly.utils.dom.createSvgElement( Blockly.utils.dom.SvgElementType.G, {}, null); - var filter = - {'filter': 'url(#' + - this.workspace_.getRenderer().getConstants().embossFilterId + ')'}; + var filter = { + 'filter': 'url(#' + + this.workspace_.getRenderer().getConstants().embossFilterId + ')' + }; if (Blockly.utils.userAgent.JAVA_FX) { // Multiple reports that JavaFX can't handle filters. // https://github.com/google/blockly/issues/99 filter = {}; } var bubbleEmboss = Blockly.utils.dom.createSvgElement( - Blockly.utils.dom.SvgElementType.G, - filter, this.bubbleGroup_); + Blockly.utils.dom.SvgElementType.G, filter, this.bubbleGroup_); this.bubbleArrow_ = Blockly.utils.dom.createSvgElement( - Blockly.utils.dom.SvgElementType.PATH, {}, - bubbleEmboss); + Blockly.utils.dom.SvgElementType.PATH, {}, bubbleEmboss); this.bubbleBack_ = Blockly.utils.dom.createSvgElement( - Blockly.utils.dom.SvgElementType.RECT, - { + Blockly.utils.dom.SvgElementType.RECT, { 'class': 'blocklyDraggable', 'x': 0, 'y': 0, @@ -263,8 +263,7 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { if (hasResize) { this.resizeGroup_ = Blockly.utils.dom.createSvgElement( Blockly.utils.dom.SvgElementType.G, - {'class': this.workspace_.RTL ? - 'blocklyResizeSW' : 'blocklyResizeSE'}, + {'class': this.workspace_.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE'}, this.bubbleGroup_); var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH; Blockly.utils.dom.createSvgElement( @@ -272,21 +271,23 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { {'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())}, this.resizeGroup_); Blockly.utils.dom.createSvgElement( - Blockly.utils.dom.SvgElementType.LINE, - { + Blockly.utils.dom.SvgElementType.LINE, { 'class': 'blocklyResizeLine', - 'x1': resizeSize / 3, 'y1': resizeSize - 1, - 'x2': resizeSize - 1, 'y2': resizeSize / 3 - }, this.resizeGroup_); + 'x1': resizeSize / 3, + 'y1': resizeSize - 1, + 'x2': resizeSize - 1, + 'y2': resizeSize / 3 + }, + this.resizeGroup_); Blockly.utils.dom.createSvgElement( - Blockly.utils.dom.SvgElementType.LINE, - { + Blockly.utils.dom.SvgElementType.LINE, { 'class': 'blocklyResizeLine', 'x1': resizeSize * 2 / 3, 'y1': resizeSize - 1, 'x2': resizeSize - 1, 'y2': resizeSize * 2 / 3 - }, this.resizeGroup_); + }, + this.resizeGroup_); } else { this.resizeGroup_ = null; } @@ -305,7 +306,7 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { /** * Return the root node of the bubble's SVG group. - * @return {SVGElement} The root SVG node of the bubble's group. + * @return {!SVGElement} The root SVG node of the bubble's group. */ Blockly.Bubble.prototype.getSvgRoot = function() { return this.bubbleGroup_; @@ -352,6 +353,15 @@ Blockly.Bubble.prototype.isDeletable = function() { return false; }; +/** + * Update the style of this bubble when it is dragged over a delete area. + * @param {boolean} _enable True if the bubble is about to be deleted, false + * otherwise. + */ +Blockly.Bubble.prototype.setDeleteStyle = function(_enable) { + // NOP if bubble is not deletable. +}; + /** * Handle a mouse-down on bubble's resize corner. * @param {!Event} e Mouse down event. @@ -366,13 +376,15 @@ Blockly.Bubble.prototype.resizeMouseDown_ = function(e) { return; } // Left-click (or middle click) - this.workspace_.startDrag(e, new Blockly.utils.Coordinate( - this.workspace_.RTL ? -this.width_ : this.width_, this.height_)); + this.workspace_.startDrag( + e, + new Blockly.utils.Coordinate( + this.workspace_.RTL ? -this.width_ : this.width_, this.height_)); - Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document, - 'mouseup', this, Blockly.Bubble.bubbleMouseUp_); - Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document, - 'mousemove', this, this.resizeMouseMove_); + Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_( + document, 'mouseup', this, Blockly.Bubble.bubbleMouseUp_); + Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_( + document, 'mousemove', this, this.resizeMouseMove_); Blockly.hideChaff(); // This event has been handled. No need to bubble up to the document. e.stopPropagation(); @@ -470,8 +482,9 @@ Blockly.Bubble.prototype.layoutBubble_ = function() { // Set the position to whichever position shows the most of the bubble, // with tiebreaks going in the order: top > start > close > far. - var mostOverlap = Math.max(topPositionOverlap, startPositionOverlap, - closerPositionOverlap, fartherPositionOverlap); + var mostOverlap = Math.max( + topPositionOverlap, startPositionOverlap, closerPositionOverlap, + fartherPositionOverlap); if (topPositionOverlap == mostOverlap) { this.relativeLeft_ = topPosition.x; this.relativeTop_ = topPosition.y; @@ -508,14 +521,11 @@ Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, metrics) { // The position of the top-left corner of the bubble in workspace units. var bubbleMin = { x: this.workspace_.RTL ? (this.anchorXY_.x - relativeMin.x - this.width_) : - (relativeMin.x + this.anchorXY_.x), + (relativeMin.x + this.anchorXY_.x), y: relativeMin.y + this.anchorXY_.y }; // The position of the bottom-right corner of the bubble in workspace units. - var bubbleMax = { - x: bubbleMin.x + this.width_, - y: bubbleMin.y + this.height_ - }; + var bubbleMax = {x: bubbleMin.x + this.width_, y: bubbleMin.y + this.height_}; // We could adjust these values to account for the scrollbars, but the // bubbles should have been adjusted to not collide with them anyway, so @@ -523,10 +533,7 @@ Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, metrics) { // calculation. // The position of the top-left corner of the workspace. - var workspaceMin = { - x: metrics.viewLeft, - y: metrics.viewTop - }; + var workspaceMin = {x: metrics.viewLeft, y: metrics.viewTop}; // The position of the bottom-right corner of the workspace. var workspaceMax = { x: metrics.viewLeft + metrics.viewWidth, @@ -537,8 +544,10 @@ Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, metrics) { Math.max(bubbleMin.x, workspaceMin.x); var overlapHeight = Math.min(bubbleMax.y, workspaceMax.y) - Math.max(bubbleMin.y, workspaceMin.y); - return Math.max(0, Math.min(1, - (overlapWidth * overlapHeight) / (this.width_ * this.height_))); + return Math.max( + 0, + Math.min( + 1, (overlapWidth * overlapHeight) / (this.width_ * this.height_))); }; /** @@ -574,8 +583,8 @@ Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(metrics) { var workspaceLeft = metrics.viewLeft; var workspaceRight = metrics.viewLeft + metrics.viewWidth - - // Thickness in workspace units. - (Blockly.Scrollbar.scrollbarThickness / this.workspace_.scale); + // Thickness in workspace units. + (Blockly.Scrollbar.scrollbarThickness / this.workspace_.scale); } if (this.workspace_.RTL) { @@ -698,12 +707,15 @@ Blockly.Bubble.prototype.setBubbleSize = function(width, height) { if (this.workspace_.RTL) { // Mirror the resize group. var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH; - this.resizeGroup_.setAttribute('transform', 'translate(' + - resizeSize + ',' + (height - doubleBorderWidth) + ') scale(-1 1)'); + this.resizeGroup_.setAttribute( + 'transform', + 'translate(' + resizeSize + ',' + (height - doubleBorderWidth) + + ') scale(-1 1)'); } else { - this.resizeGroup_.setAttribute('transform', 'translate(' + - (width - doubleBorderWidth) + ',' + - (height - doubleBorderWidth) + ')'); + this.resizeGroup_.setAttribute( + 'transform', + 'translate(' + (width - doubleBorderWidth) + ',' + + (height - doubleBorderWidth) + ')'); } } if (this.autoLayout_) { @@ -756,8 +768,8 @@ Blockly.Bubble.prototype.renderArrow_ = function() { // Calculate the thickness of the base of the arrow. var bubbleSize = this.getBubbleSize(); - var thickness = (bubbleSize.width + bubbleSize.height) / - Blockly.Bubble.ARROW_THICKNESS; + var thickness = + (bubbleSize.width + bubbleSize.height) / Blockly.Bubble.ARROW_THICKNESS; thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 4; // Back the tip of the arrow off of the anchor. @@ -776,18 +788,18 @@ Blockly.Bubble.prototype.renderArrow_ = function() { if (swirlAngle > Math.PI * 2) { swirlAngle -= Math.PI * 2; } - var swirlRise = Math.sin(swirlAngle) * - hypotenuse / Blockly.Bubble.ARROW_BEND; - var swirlRun = Math.cos(swirlAngle) * - hypotenuse / Blockly.Bubble.ARROW_BEND; + var swirlRise = + Math.sin(swirlAngle) * hypotenuse / Blockly.Bubble.ARROW_BEND; + var swirlRun = + Math.cos(swirlAngle) * hypotenuse / Blockly.Bubble.ARROW_BEND; steps.push('M' + baseX1 + ',' + baseY1); - steps.push('C' + (baseX1 + swirlRun) + ',' + (baseY1 + swirlRise) + - ' ' + relAnchorX + ',' + relAnchorY + - ' ' + relAnchorX + ',' + relAnchorY); - steps.push('C' + relAnchorX + ',' + relAnchorY + - ' ' + (baseX2 + swirlRun) + ',' + (baseY2 + swirlRise) + - ' ' + baseX2 + ',' + baseY2); + steps.push( + 'C' + (baseX1 + swirlRun) + ',' + (baseY1 + swirlRise) + ' ' + + relAnchorX + ',' + relAnchorY + ' ' + relAnchorX + ',' + relAnchorY); + steps.push( + 'C' + relAnchorX + ',' + relAnchorY + ' ' + (baseX2 + swirlRun) + ',' + + (baseY2 + swirlRise) + ' ' + baseX2 + ',' + baseY2); } steps.push('z'); this.bubbleArrow_.setAttribute('d', steps.join(' ')); @@ -849,8 +861,8 @@ Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) { Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() { return new Blockly.utils.Coordinate( this.workspace_.RTL ? - -this.relativeLeft_ + this.anchorXY_.x - this.width_ : - this.anchorXY_.x + this.relativeLeft_, + -this.relativeLeft_ + this.anchorXY_.x - this.width_ : + this.anchorXY_.x + this.relativeLeft_, this.anchorXY_.y + this.relativeTop_); }; diff --git a/core/bubble_dragger.js b/core/bubble_dragger.js index 66c16459e..11cbda32f 100644 --- a/core/bubble_dragger.js +++ b/core/bubble_dragger.js @@ -18,20 +18,21 @@ goog.require('Blockly.Events.CommentMove'); goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); +goog.requireType('Blockly.IBubble'); + /** * Class for a bubble dragger. It moves things on the bubble canvas around the * workspace when they are being dragged by a mouse or touch. These can be * block comments, mutators, warnings, or workspace comments. - * @param {!Blockly.Bubble|!Blockly.WorkspaceCommentSvg} bubble The item on the - * bubble canvas to drag. + * @param {!Blockly.IBubble} bubble The item on the bubble canvas to drag. * @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on. * @constructor */ Blockly.BubbleDragger = function(bubble, workspace) { /** * The item on the bubble canvas that is being dragged. - * @type {!Blockly.Bubble|!Blockly.WorkspaceCommentSvg} + * @type {!Blockly.IBubble} * @private */ this.draggingBubble_ = bubble; @@ -75,7 +76,8 @@ Blockly.BubbleDragger = function(bubble, workspace) { */ this.dragSurface_ = Blockly.utils.is3dSupported() && !!workspace.getBlockDragSurface() ? - workspace.getBlockDragSurface() : null; + workspace.getBlockDragSurface() : + null; }; /** @@ -109,7 +111,7 @@ Blockly.BubbleDragger.prototype.startBubbleDrag = function() { var toolbox = this.workspace_.getToolbox(); if (toolbox && typeof toolbox.addStyle == 'function') { var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; + 'blocklyToolboxGrab'; toolbox.addStyle(style); } }; @@ -211,7 +213,7 @@ Blockly.BubbleDragger.prototype.endBubbleDrag = function( var toolbox = this.workspace_.getToolbox(); if (toolbox && typeof toolbox.removeStyle == 'function') { var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; + 'blocklyToolboxGrab'; toolbox.removeStyle(style); } Blockly.Events.setGroup(false); @@ -238,14 +240,15 @@ Blockly.BubbleDragger.prototype.fireMoveEvent_ = function() { * correction for mutator workspaces. * This function does not consider differing origins. It simply scales the * input's x and y values. - * @param {!Blockly.utils.Coordinate} pixelCoord A coordinate with x and y values - * in CSS pixel units. - * @return {!Blockly.utils.Coordinate} The input coordinate divided by the workspace - * scale. + * @param {!Blockly.utils.Coordinate} pixelCoord A coordinate with x and y + * values in CSS pixel units. + * @return {!Blockly.utils.Coordinate} The input coordinate divided by the + * workspace scale. * @private */ Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) { - var result = new Blockly.utils.Coordinate(pixelCoord.x / this.workspace_.scale, + var result = new Blockly.utils.Coordinate( + pixelCoord.x / this.workspace_.scale, pixelCoord.y / this.workspace_.scale); if (this.workspace_.isMutator) { // If we're in a mutator, its scale is always 1, purely because of some diff --git a/core/gesture.js b/core/gesture.js index 3d5c933fe..160724f91 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -28,6 +28,7 @@ goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.WorkspaceDragger'); +goog.requireType('Blockly.IBubble'); goog.requireType('Blockly.IFlyout'); @@ -45,7 +46,6 @@ goog.requireType('Blockly.IFlyout'); * @constructor */ Blockly.Gesture = function(e, creatorWorkspace) { - /** * The position of the mouse when the gesture started. Units are CSS pixels, * with (0, 0) at the top left of the browser window (mouseEvent clientX/Y). @@ -65,7 +65,7 @@ Blockly.Gesture = function(e, creatorWorkspace) { /** * The bubble that the gesture started on, or null if it did not start on a * bubble. - * @type {Blockly.Bubble} + * @type {Blockly.IBubble} * @private */ this.startBubble_ = null; @@ -280,16 +280,17 @@ Blockly.Gesture.prototype.updateFromEvent_ = function(e) { * @private */ Blockly.Gesture.prototype.updateDragDelta_ = function(currentXY) { - this.currentDragDeltaXY_ = Blockly.utils.Coordinate.difference(currentXY, + this.currentDragDeltaXY_ = Blockly.utils.Coordinate.difference( + currentXY, /** @type {!Blockly.utils.Coordinate} */ (this.mouseDownXY_)); if (!this.hasExceededDragRadius_) { - var currentDragDelta = Blockly.utils.Coordinate.magnitude( - this.currentDragDeltaXY_); + var currentDragDelta = + Blockly.utils.Coordinate.magnitude(this.currentDragDeltaXY_); // The flyout has a different drag radius from the rest of Blockly. - var limitRadius = this.flyout_ ? Blockly.FLYOUT_DRAG_RADIUS : - Blockly.DRAG_RADIUS; + var limitRadius = + this.flyout_ ? Blockly.FLYOUT_DRAG_RADIUS : Blockly.DRAG_RADIUS; this.hasExceededDragRadius_ = currentDragDelta > limitRadius; return this.hasExceededDragRadius_; @@ -387,7 +388,8 @@ Blockly.Gesture.prototype.updateIsDraggingBlock_ = function() { * @private */ Blockly.Gesture.prototype.updateIsDraggingWorkspace_ = function() { - var wsMovable = this.flyout_ ? this.flyout_.isScrollable() : + var wsMovable = this.flyout_ ? + this.flyout_.isScrollable() : this.startWorkspace_ && this.startWorkspace_.isDraggable(); if (!wsMovable) { @@ -439,8 +441,7 @@ Blockly.Gesture.prototype.startDraggingBlock_ = function() { /** @type {!Blockly.BlockSvg} */ (this.targetBlock_), /** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_)); this.blockDragger_.startBlockDrag(this.currentDragDeltaXY_, this.healStack_); - this.blockDragger_.dragBlock(this.mostRecentEvent_, - this.currentDragDeltaXY_); + this.blockDragger_.dragBlock(this.mostRecentEvent_, this.currentDragDeltaXY_); }; /** @@ -450,11 +451,11 @@ Blockly.Gesture.prototype.startDraggingBlock_ = function() { // TODO (fenichel): Possibly combine this and startDraggingBlock_. Blockly.Gesture.prototype.startDraggingBubble_ = function() { this.bubbleDragger_ = new Blockly.BubbleDragger( - /** @type {!Blockly.Bubble} */ (this.startBubble_), + /** @type {!Blockly.IBubble} */ (this.startBubble_), /** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_)); this.bubbleDragger_.startBubbleDrag(); - this.bubbleDragger_.dragBubble(this.mostRecentEvent_, - this.currentDragDeltaXY_); + this.bubbleDragger_.dragBubble( + this.mostRecentEvent_, this.currentDragDeltaXY_); }; /** * Start a gesture: update the workspace to indicate that a gesture is in @@ -487,8 +488,7 @@ Blockly.Gesture.prototype.doStart = function(e) { Blockly.Tooltip.block(); if (this.targetBlock_) { - if (!this.targetBlock_.isInFlyout && - e.shiftKey && + if (!this.targetBlock_.isInFlyout && e.shiftKey && this.targetBlock_.workspace.keyboardAccessibilityMode) { this.creatorWorkspace_.getCursor().setCurNode( Blockly.ASTNode.createTopNode(this.targetBlock_)); @@ -503,8 +503,8 @@ Blockly.Gesture.prototype.doStart = function(e) { } if ((e.type.toLowerCase() == 'touchstart' || - e.type.toLowerCase() == 'pointerdown') && - e.pointerType != 'mouse') { + e.type.toLowerCase() == 'pointerdown') && + e.pointerType != 'mouse') { Blockly.longStart(e, this); } @@ -539,11 +539,11 @@ Blockly.Gesture.prototype.handleMove = function(e) { if (this.isDraggingWorkspace_) { this.workspaceDragger_.drag(this.currentDragDeltaXY_); } else if (this.isDraggingBlock_) { - this.blockDragger_.dragBlock(this.mostRecentEvent_, - this.currentDragDeltaXY_); + this.blockDragger_.dragBlock( + this.mostRecentEvent_, this.currentDragDeltaXY_); } else if (this.isDraggingBubble_) { - this.bubbleDragger_.dragBubble(this.mostRecentEvent_, - this.currentDragDeltaXY_); + this.bubbleDragger_.dragBubble( + this.mostRecentEvent_, this.currentDragDeltaXY_); } e.preventDefault(); e.stopPropagation(); @@ -604,11 +604,11 @@ Blockly.Gesture.prototype.cancel = function() { } Blockly.longStop_(); if (this.isDraggingBubble_) { - this.bubbleDragger_.endBubbleDrag(this.mostRecentEvent_, - this.currentDragDeltaXY_); + this.bubbleDragger_.endBubbleDrag( + this.mostRecentEvent_, this.currentDragDeltaXY_); } else if (this.isDraggingBlock_) { - this.blockDragger_.endBlockDrag(this.mostRecentEvent_, - this.currentDragDeltaXY_); + this.blockDragger_.endBlockDrag( + this.mostRecentEvent_, this.currentDragDeltaXY_); } else if (this.isDraggingWorkspace_) { this.workspaceDragger_.endDrag(this.currentDragDeltaXY_); } @@ -647,7 +647,8 @@ Blockly.Gesture.prototype.handleRightClick = function(e) { */ Blockly.Gesture.prototype.handleWsStart = function(e, ws) { if (this.hasStarted_) { - throw Error('Tried to call gesture.handleWsStart, ' + + throw Error( + 'Tried to call gesture.handleWsStart, ' + 'but the gesture had already been started.'); } this.setStartWorkspace_(ws); @@ -677,7 +678,8 @@ Blockly.Gesture.prototype.fireWorkspaceClick_ = function(ws) { */ Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) { if (this.hasStarted_) { - throw Error('Tried to call gesture.handleFlyoutStart, ' + + throw Error( + 'Tried to call gesture.handleFlyoutStart, ' + 'but the gesture had already been started.'); } this.setStartFlyout_(flyout); @@ -692,7 +694,8 @@ Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) { */ Blockly.Gesture.prototype.handleBlockStart = function(e, block) { if (this.hasStarted_) { - throw Error('Tried to call gesture.handleBlockStart, ' + + throw Error( + 'Tried to call gesture.handleBlockStart, ' + 'but the gesture had already been started.'); } this.setStartBlock(block); @@ -702,12 +705,13 @@ Blockly.Gesture.prototype.handleBlockStart = function(e, block) { /** * Handle a mousedown/touchstart event on a bubble. * @param {!Event} e A mouse down or touch start event. - * @param {!Blockly.Bubble} bubble The bubble the event hit. + * @param {!Blockly.IBubble} bubble The bubble the event hit. * @package */ Blockly.Gesture.prototype.handleBubbleStart = function(e, bubble) { if (this.hasStarted_) { - throw Error('Tried to call gesture.handleBubbleStart, ' + + throw Error( + 'Tried to call gesture.handleBubbleStart, ' + 'but the gesture had already been started.'); } this.setStartBubble(bubble); @@ -804,7 +808,8 @@ Blockly.Gesture.prototype.bringBlockToFront_ = function() { */ Blockly.Gesture.prototype.setStartField = function(field) { if (this.hasStarted_) { - throw Error('Tried to call gesture.setStartField, ' + + throw Error( + 'Tried to call gesture.setStartField, ' + 'but the gesture had already been started.'); } if (!this.startField_) { @@ -814,7 +819,7 @@ Blockly.Gesture.prototype.setStartField = function(field) { /** * Record the bubble that a gesture started on - * @param {Blockly.Bubble} bubble The bubble the gesture started on. + * @param {Blockly.IBubble} bubble The bubble the gesture started on. * @package */ Blockly.Gesture.prototype.setStartBubble = function(bubble) { @@ -916,8 +921,8 @@ Blockly.Gesture.prototype.isBlockClick_ = function() { * @private */ Blockly.Gesture.prototype.isFieldClick_ = function() { - var fieldClickable = this.startField_ ? - this.startField_.isClickable() : false; + var fieldClickable = + this.startField_ ? this.startField_.isClickable() : false; return fieldClickable && !this.hasExceededDragRadius_ && (!this.flyout_ || !this.flyout_.autoClose); }; @@ -929,8 +934,8 @@ Blockly.Gesture.prototype.isFieldClick_ = function() { * @private */ Blockly.Gesture.prototype.isWorkspaceClick_ = function() { - var onlyTouchedWorkspace = !this.startBlock_ && !this.startBubble_ && - !this.startField_; + var onlyTouchedWorkspace = + !this.startBlock_ && !this.startBubble_ && !this.startField_; return onlyTouchedWorkspace && !this.hasExceededDragRadius_; }; diff --git a/core/interfaces/i_bubble.js b/core/interfaces/i_bubble.js new file mode 100644 index 000000000..bbf9fdae3 --- /dev/null +++ b/core/interfaces/i_bubble.js @@ -0,0 +1,83 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview The interface for a bubble. + * @author samelh@google.com (Sam El-Husseini) + */ + +'use strict'; + +goog.provide('Blockly.IBubble'); + +goog.requireType('Blockly.IContextMenu'); +goog.requireType('Blockly.IDeletable'); + + +/** + * A bubble interface. + * @interface + * @extends {Blockly.IDeletable} + * @extends {Blockly.IContextMenu} + */ +Blockly.IBubble = function() {}; + +/** + * Return the coordinates of the top-left corner of this bubble's body relative + * to the drawing surface's origin (0,0), in workspace units. + * @return {!Blockly.utils.Coordinate} Object with .x and .y properties. + */ +Blockly.IBubble.prototype.getRelativeToSurfaceXY; + +/** + * Return the root node of the bubble's SVG group. + * @return {!SVGElement} The root SVG node of the bubble's group. + */ +Blockly.IBubble.prototype.getSvgRoot; + +/** + * Set whether auto-layout of this bubble is enabled. The first time a bubble + * is shown it positions itself to not cover any blocks. Once a user has + * dragged it to reposition, it renders where the user put it. + * @param {boolean} enable True if auto-layout should be enabled, false + * otherwise. + */ +Blockly.IBubble.prototype.setAutoLayout; + +/** + * Triggers a move callback if one exists at the end of a drag. + * @param {boolean} adding True if adding, false if removing. + */ +Blockly.IBubble.prototype.setDragging; + +/** + * Move this bubble during a drag, taking into account whether or not there is + * a drag surface. + * @param {Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries + * rendered items during a drag, or null if no drag surface is in use. + * @param {!Blockly.utils.Coordinate} newLoc The location to translate to, in + * workspace coordinates. + */ +Blockly.IBubble.prototype.moveDuringDrag; + +/** + * Move the bubble to the specified location in workspace coordinates. + * @param {number} x The x position to move to. + * @param {number} y The y position to move to. + */ +Blockly.IBubble.prototype.moveTo; + +/** + * Update the style of this bubble when it is dragged over a delete area. + * @param {boolean} enable True if the bubble is about to be deleted, false + * otherwise. + */ +Blockly.IBubble.prototype.setDeleteStyle; + +/** + * Dispose of this bubble. + */ +Blockly.IBubble.prototype.dispose; diff --git a/core/interfaces/i_contextmenu.js b/core/interfaces/i_contextmenu.js new file mode 100644 index 000000000..fcc406b05 --- /dev/null +++ b/core/interfaces/i_contextmenu.js @@ -0,0 +1,26 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview The interface for an object that supports a right-click. + * @author samelh@google.com (Sam El-Husseini) + */ + +'use strict'; + +goog.provide('Blockly.IContextMenu'); + + +/** + * @interface + */ +Blockly.IContextMenu = function() {}; + +/** + * Show the context menu for this object. + * @param {!Event} e Mouse event. + */ +Blockly.IContextMenu.prototype.showContextMenu; diff --git a/core/workspace_comment_svg.js b/core/workspace_comment_svg.js index 62fbb93ed..da6623890 100644 --- a/core/workspace_comment_svg.js +++ b/core/workspace_comment_svg.js @@ -26,6 +26,7 @@ goog.require('Blockly.utils.Rect'); goog.require('Blockly.WorkspaceComment'); goog.requireType('Blockly.IBoundedElement'); +goog.requireType('Blockly.IBubble'); goog.requireType('Blockly.ICopyable'); /** @@ -38,12 +39,12 @@ goog.requireType('Blockly.ICopyable'); * create a new ID. * @extends {Blockly.WorkspaceComment} * @implements {Blockly.IBoundedElement} + * @implements {Blockly.IBubble} * @implements {Blockly.ICopyable} * @constructor */ -Blockly.WorkspaceCommentSvg = function(workspace, content, height, width, - opt_id) { - +Blockly.WorkspaceCommentSvg = function( + workspace, content, height, width, opt_id) { /** * Mouse up event data. * @type {?Blockly.EventData} @@ -60,7 +61,7 @@ Blockly.WorkspaceCommentSvg = function(workspace, content, height, width, // Create core elements for the block. /** - * @type {SVGElement} + * @type {!SVGElement} * @private */ this.svgGroup_ = Blockly.utils.dom.createSvgElement( @@ -68,8 +69,7 @@ Blockly.WorkspaceCommentSvg = function(workspace, content, height, width, this.svgGroup_.translate_ = ''; this.svgRect_ = Blockly.utils.dom.createSvgElement( - Blockly.utils.dom.SvgElementType.RECT, - { + Blockly.utils.dom.SvgElementType.RECT, { 'class': 'blocklyCommentRect', 'x': 0, 'y': 0, @@ -94,13 +94,13 @@ Blockly.WorkspaceCommentSvg = function(workspace, content, height, width, this.useDragSurface_ = Blockly.utils.is3dSupported() && !!workspace.blockDragSurface_; - Blockly.WorkspaceCommentSvg.superClass_.constructor.call(this, - workspace, content, height, width, opt_id); + Blockly.WorkspaceCommentSvg.superClass_.constructor.call( + this, workspace, content, height, width, opt_id); this.render(); }; -Blockly.utils.object.inherits(Blockly.WorkspaceCommentSvg, - Blockly.WorkspaceComment); +Blockly.utils.object.inherits( + Blockly.WorkspaceCommentSvg, Blockly.WorkspaceComment); /** * The width and height to use to size a workspace comment when it is first @@ -291,8 +291,8 @@ Blockly.WorkspaceCommentSvg.prototype.getRelativeToSurfaceXY = function() { var x = 0; var y = 0; - var dragSurfaceGroup = this.useDragSurface_ ? - this.workspace.blockDragSurface_.getGroup() : null; + var dragSurfaceGroup = + this.useDragSurface_ ? this.workspace.blockDragSurface_.getGroup() : null; var element = this.getSvgRoot(); if (element) { @@ -312,7 +312,7 @@ Blockly.WorkspaceCommentSvg.prototype.getRelativeToSurfaceXY = function() { } element = element.parentNode; } while (element && element != this.workspace.getBubbleCanvas() && - element != dragSurfaceGroup); + element != dragSurfaceGroup); } this.xy_ = new Blockly.utils.Coordinate(x, y); return this.xy_; @@ -344,8 +344,7 @@ Blockly.WorkspaceCommentSvg.prototype.moveBy = function(dx, dy) { */ Blockly.WorkspaceCommentSvg.prototype.translate = function(x, y) { this.xy_ = new Blockly.utils.Coordinate(x, y); - this.getSvgRoot().setAttribute('transform', - 'translate(' + x + ',' + y + ')'); + this.getSvgRoot().setAttribute('transform', 'translate(' + x + ',' + y + ')'); }; /** @@ -373,8 +372,8 @@ Blockly.WorkspaceCommentSvg.prototype.moveToDragSurface = function() { * Move this comment back to the workspace block canvas. * Generally should be called at the same time as setDragging(false). * Does nothing if useDragSurface_ is false. - * @param {!Blockly.utils.Coordinate} newXY The position the comment should take on - * on the workspace canvas, in workspace coordinates. + * @param {!Blockly.utils.Coordinate} newXY The position the comment should take + * on on the workspace canvas, in workspace coordinates. * @private */ Blockly.WorkspaceCommentSvg.prototype.moveOffDragSurface = function(newXY) { @@ -395,14 +394,14 @@ Blockly.WorkspaceCommentSvg.prototype.moveOffDragSurface = function(newXY) { * workspace coordinates. * @package */ -Blockly.WorkspaceCommentSvg.prototype.moveDuringDrag = function(dragSurface, - newLoc) { +Blockly.WorkspaceCommentSvg.prototype.moveDuringDrag = function( + dragSurface, newLoc) { if (dragSurface) { dragSurface.translateSurface(newLoc.x, newLoc.y); } else { this.svgGroup_.translate_ = 'translate(' + newLoc.x + ',' + newLoc.y + ')'; - this.svgGroup_.setAttribute('transform', - this.svgGroup_.translate_ + this.svgGroup_.skew_); + this.svgGroup_.setAttribute( + 'transform', this.svgGroup_.translate_ + this.svgGroup_.skew_); } }; @@ -507,7 +506,7 @@ Blockly.WorkspaceCommentSvg.prototype.setDragging = function(adding) { /** * Return the root node of the SVG or null if none exists. - * @return {SVGElement} The root SVG node (probably a group). + * @return {!SVGElement} The root SVG node (probably a group). * @package */ Blockly.WorkspaceCommentSvg.prototype.getSvgRoot = function() { @@ -551,7 +550,15 @@ Blockly.WorkspaceCommentSvg.prototype.setDeleteStyle = function(enable) { } }; -Blockly.WorkspaceCommentSvg.prototype.setAutoLayout = function() { +/** + * Set whether auto-layout of this bubble is enabled. The first time a bubble + * is shown it positions itself to not cover any blocks. Once a user has + * dragged it to reposition, it renders where the user put it. + * @param {boolean} _enable True if auto-layout should be enabled, false + * otherwise. + * @package + */ +Blockly.WorkspaceCommentSvg.prototype.setAutoLayout = function(_enable) { // NOP for compatibility with the bubble dragger. }; @@ -564,14 +571,14 @@ Blockly.WorkspaceCommentSvg.prototype.setAutoLayout = function() { * @return {!Blockly.WorkspaceCommentSvg} The created workspace comment. * @package */ -Blockly.WorkspaceCommentSvg.fromXml = function(xmlComment, workspace, - opt_wsWidth) { +Blockly.WorkspaceCommentSvg.fromXml = function( + xmlComment, workspace, opt_wsWidth) { Blockly.Events.disable(); try { var info = Blockly.WorkspaceComment.parseAttributes(xmlComment); - var comment = new Blockly.WorkspaceCommentSvg(workspace, - info.content, info.h, info.w, info.id); + var comment = new Blockly.WorkspaceCommentSvg( + workspace, info.content, info.h, info.w, info.id); if (workspace.rendered) { comment.initSvg(); comment.render(false); @@ -608,8 +615,8 @@ Blockly.WorkspaceCommentSvg.prototype.toXmlWithXY = function(opt_noId) { } var element = this.toXml(opt_noId); var xy = this.getRelativeToSurfaceXY(); - element.setAttribute('x', - Math.round(this.workspace.RTL ? width - xy.x : xy.x)); + element.setAttribute( + 'x', Math.round(this.workspace.RTL ? width - xy.x : xy.x)); element.setAttribute('y', Math.round(xy.y)); element.setAttribute('h', this.getHeight()); element.setAttribute('w', this.getWidth()); @@ -622,17 +629,14 @@ Blockly.WorkspaceCommentSvg.prototype.toXmlWithXY = function(opt_noId) { * @package */ Blockly.WorkspaceCommentSvg.prototype.toCopyData = function() { - return { - xml: this.toXmlWithXY(), - source: this.workspace, - typeCounts: null - }; + return {xml: this.toXmlWithXY(), source: this.workspace, typeCounts: null}; }; /** * CSS for workspace comment. See css.js for use. */ Blockly.Css.register([ + // clang-format off /* eslint-disable indent */ '.blocklyCommentForeignObject {', 'position: relative;', @@ -692,4 +696,5 @@ Blockly.Css.register([ 'stroke: #fc3;', '}' /* eslint-enable indent */ + // clang-format on ]);