File "gesture.js"
Full path: /usr/home/mndrn/domains/mndrn.ru/public_html/block-hill/blockly/core/gesture.js
File size: 29.26 KiB (29967 bytes)
MIME-type: text/plain
Charset: utf-8
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The class representing an in-progress gesture, usually a drag
* or a tap.
* @author [email protected] (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.Gesture');
goog.require('Blockly.ASTNode');
goog.require('Blockly.blockAnimations');
goog.require('Blockly.BlockDragger');
goog.require('Blockly.BubbleDragger');
goog.require('Blockly.constants');
goog.require('Blockly.Events');
goog.require('Blockly.Events.Ui');
goog.require('Blockly.FlyoutDragger');
goog.require('Blockly.navigation');
goog.require('Blockly.Tooltip');
goog.require('Blockly.Touch');
goog.require('Blockly.utils');
goog.require('Blockly.utils.Coordinate');
goog.require('Blockly.WorkspaceDragger');
goog.requireType('Blockly.IBubble');
goog.requireType('Blockly.IFlyout');
/**
* Note: In this file "start" refers to touchstart, mousedown, and pointerstart
* events. "End" refers to touchend, mouseup, and pointerend events.
*/
// TODO: Consider touchcancel/pointercancel.
/**
* Class for one gesture.
* @param {!Event} e The event that kicked off this gesture.
* @param {!Blockly.WorkspaceSvg} creatorWorkspace The workspace that created
* this gesture and has a reference to it.
* @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).
* @type {Blockly.utils.Coordinate}
* @private
*/
this.mouseDownXY_ = null;
/**
* How far the mouse has moved during this drag, in pixel units.
* (0, 0) is at this.mouseDownXY_.
* @type {!Blockly.utils.Coordinate}
* @private
*/
this.currentDragDeltaXY_ = new Blockly.utils.Coordinate(0, 0);
/**
* The bubble that the gesture started on, or null if it did not start on a
* bubble.
* @type {Blockly.IBubble}
* @private
*/
this.startBubble_ = null;
/**
* The field that the gesture started on, or null if it did not start on a
* field.
* @type {Blockly.Field}
* @private
*/
this.startField_ = null;
/**
* The block that the gesture started on, or null if it did not start on a
* block.
* @type {Blockly.BlockSvg}
* @private
*/
this.startBlock_ = null;
/**
* The block that this gesture targets. If the gesture started on a
* shadow block, this is the first non-shadow parent of the block. If the
* gesture started in the flyout, this is the root block of the block group
* that was clicked or dragged.
* @type {Blockly.BlockSvg}
* @private
*/
this.targetBlock_ = null;
/**
* The workspace that the gesture started on. There may be multiple
* workspaces on a page; this is more accurate than using
* Blockly.getMainWorkspace().
* @type {Blockly.WorkspaceSvg}
* @protected
*/
this.startWorkspace_ = null;
/**
* The workspace that created this gesture. This workspace keeps a reference
* to the gesture, which will need to be cleared at deletion.
* This may be different from the start workspace. For instance, a flyout is
* a workspace, but its parent workspace manages gestures for it.
* @type {!Blockly.WorkspaceSvg}
* @private
*/
this.creatorWorkspace_ = creatorWorkspace;
/**
* Whether the pointer has at any point moved out of the drag radius.
* A gesture that exceeds the drag radius is a drag even if it ends exactly
* at its start point.
* @type {boolean}
* @private
*/
this.hasExceededDragRadius_ = false;
/**
* Whether the workspace is currently being dragged.
* @type {boolean}
* @private
*/
this.isDraggingWorkspace_ = false;
/**
* Whether the block is currently being dragged.
* @type {boolean}
* @private
*/
this.isDraggingBlock_ = false;
/**
* Whether the bubble is currently being dragged.
* @type {boolean}
* @private
*/
this.isDraggingBubble_ = false;
/**
* The event that most recently updated this gesture.
* @type {!Event}
* @private
*/
this.mostRecentEvent_ = e;
/**
* A handle to use to unbind a mouse move listener at the end of a drag.
* Opaque data returned from Blockly.bindEventWithChecks_.
* @type {?Blockly.EventData}
* @protected
*/
this.onMoveWrapper_ = null;
/**
* A handle to use to unbind a mouse up listener at the end of a drag.
* Opaque data returned from Blockly.bindEventWithChecks_.
* @type {?Blockly.EventData}
* @protected
*/
this.onUpWrapper_ = null;
/**
* The object tracking a bubble drag, or null if none is in progress.
* @type {Blockly.BubbleDragger}
* @private
*/
this.bubbleDragger_ = null;
/**
* The object tracking a block drag, or null if none is in progress.
* @type {Blockly.BlockDragger}
* @private
*/
this.blockDragger_ = null;
/**
* The object tracking a workspace or flyout workspace drag, or null if none
* is in progress.
* @type {Blockly.WorkspaceDragger}
* @private
*/
this.workspaceDragger_ = null;
/**
* The flyout a gesture started in, if any.
* @type {Blockly.IFlyout}
* @private
*/
this.flyout_ = null;
/**
* Boolean for sanity-checking that some code is only called once.
* @type {boolean}
* @private
*/
this.calledUpdateIsDragging_ = false;
/**
* Boolean for sanity-checking that some code is only called once.
* @type {boolean}
* @private
*/
this.hasStarted_ = false;
/**
* Boolean used internally to break a cycle in disposal.
* @type {boolean}
* @protected
*/
this.isEnding_ = false;
/**
* Boolean used to indicate whether or not to heal the stack after
* disconnecting a block.
* @type {boolean}
* @private
*/
this.healStack_ = !Blockly.DRAG_STACK;
};
/**
* Sever all links from this object.
* @package
*/
Blockly.Gesture.prototype.dispose = function() {
Blockly.Touch.clearTouchIdentifier();
Blockly.Tooltip.unblock();
// Clear the owner's reference to this gesture.
this.creatorWorkspace_.clearGesture();
if (this.onMoveWrapper_) {
Blockly.unbindEvent_(this.onMoveWrapper_);
}
if (this.onUpWrapper_) {
Blockly.unbindEvent_(this.onUpWrapper_);
}
if (this.blockDragger_) {
this.blockDragger_.dispose();
}
if (this.workspaceDragger_) {
this.workspaceDragger_.dispose();
}
if (this.bubbleDragger_) {
this.bubbleDragger_.dispose();
}
};
/**
* Update internal state based on an event.
* @param {!Event} e The most recent mouse or touch event.
* @private
*/
Blockly.Gesture.prototype.updateFromEvent_ = function(e) {
var currentXY = new Blockly.utils.Coordinate(e.clientX, e.clientY);
var changed = this.updateDragDelta_(currentXY);
// Exceeded the drag radius for the first time.
if (changed) {
this.updateIsDragging_();
Blockly.longStop_();
}
this.mostRecentEvent_ = e;
};
/**
* DO MATH to set currentDragDeltaXY_ based on the most recent mouse position.
* @param {!Blockly.utils.Coordinate} currentXY The most recent mouse/pointer
* position, in pixel units, with (0, 0) at the window's top left corner.
* @return {boolean} True if the drag just exceeded the drag radius for the
* first time.
* @private
*/
Blockly.Gesture.prototype.updateDragDelta_ = function(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_);
// The flyout has a different drag radius from the rest of Blockly.
var limitRadius =
this.flyout_ ? Blockly.FLYOUT_DRAG_RADIUS : Blockly.DRAG_RADIUS;
this.hasExceededDragRadius_ = currentDragDelta > limitRadius;
return this.hasExceededDragRadius_;
}
return false;
};
/**
* Update this gesture to record whether a block is being dragged from the
* flyout.
* This function should be called on a mouse/touch move event the first time the
* drag radius is exceeded. It should be called no more than once per gesture.
* If a block should be dragged from the flyout this function creates the new
* block on the main workspace and updates targetBlock_ and startWorkspace_.
* @return {boolean} True if a block is being dragged from the flyout.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() {
if (!this.targetBlock_) {
return false;
}
if (!this.flyout_.isBlockCreatable_(this.targetBlock_)) {
return false;
}
if (!this.flyout_.isScrollable() ||
this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)) {
this.startWorkspace_ = this.flyout_.targetWorkspace;
this.startWorkspace_.updateScreenCalculationsIfScrolled();
// Start the event group now, so that the same event group is used for block
// creation and block dragging.
if (!Blockly.Events.getGroup()) {
Blockly.Events.setGroup(true);
}
// The start block is no longer relevant, because this is a drag.
this.startBlock_ = null;
this.targetBlock_ = this.flyout_.createBlock(this.targetBlock_);
this.targetBlock_.select();
return true;
}
return false;
};
/**
* Update this gesture to record whether a bubble is being dragged.
* This function should be called on a mouse/touch move event the first time the
* drag radius is exceeded. It should be called no more than once per gesture.
* If a bubble should be dragged this function creates the necessary
* BubbleDragger and starts the drag.
* @return {boolean} True if a bubble is being dragged.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingBubble_ = function() {
if (!this.startBubble_) {
return false;
}
this.isDraggingBubble_ = true;
this.startDraggingBubble_();
return true;
};
/**
* Update this gesture to record whether a block is being dragged.
* This function should be called on a mouse/touch move event the first time the
* drag radius is exceeded. It should be called no more than once per gesture.
* If a block should be dragged, either from the flyout or in the workspace,
* this function creates the necessary BlockDragger and starts the drag.
* @return {boolean} True if a block is being dragged.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingBlock_ = function() {
if (!this.targetBlock_) {
return false;
}
if (this.flyout_) {
this.isDraggingBlock_ = this.updateIsDraggingFromFlyout_();
} else if (this.targetBlock_.isMovable()) {
this.isDraggingBlock_ = true;
}
if (this.isDraggingBlock_) {
this.startDraggingBlock_();
return true;
}
return false;
};
/**
* Update this gesture to record whether a workspace is being dragged.
* This function should be called on a mouse/touch move event the first time the
* drag radius is exceeded. It should be called no more than once per gesture.
* If a workspace is being dragged this function creates the necessary
* WorkspaceDragger or FlyoutDragger and starts the drag.
* @private
*/
Blockly.Gesture.prototype.updateIsDraggingWorkspace_ = function() {
var wsMovable = this.flyout_ ?
this.flyout_.isScrollable() :
this.startWorkspace_ && this.startWorkspace_.isDraggable();
if (!wsMovable) {
return;
}
if (this.flyout_) {
this.workspaceDragger_ = new Blockly.FlyoutDragger(this.flyout_);
} else {
this.workspaceDragger_ = new Blockly.WorkspaceDragger(
/** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_));
}
this.isDraggingWorkspace_ = true;
this.workspaceDragger_.startDrag();
};
/**
* Update this gesture to record whether anything is being dragged.
* This function should be called on a mouse/touch move event the first time the
* drag radius is exceeded. It should be called no more than once per gesture.
* @private
*/
Blockly.Gesture.prototype.updateIsDragging_ = function() {
// Sanity check.
if (this.calledUpdateIsDragging_) {
throw Error('updateIsDragging_ should only be called once per gesture.');
}
this.calledUpdateIsDragging_ = true;
// First check if it was a bubble drag. Bubbles always sit on top of blocks.
if (this.updateIsDraggingBubble_()) {
return;
}
// Then check if it was a block drag.
if (this.updateIsDraggingBlock_()) {
return;
}
// Then check if it's a workspace drag.
this.updateIsDraggingWorkspace_();
};
/**
* Create a block dragger and start dragging the selected block.
* @private
*/
Blockly.Gesture.prototype.startDraggingBlock_ = function() {
this.blockDragger_ = new Blockly.BlockDragger(
/** @type {!Blockly.BlockSvg} */ (this.targetBlock_),
/** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_));
this.blockDragger_.startBlockDrag(this.currentDragDeltaXY_, this.healStack_);
this.blockDragger_.dragBlock(this.mostRecentEvent_, this.currentDragDeltaXY_);
};
/**
* Create a bubble dragger and start dragging the selected bubble.
* @private
*/
// TODO (fenichel): Possibly combine this and startDraggingBlock_.
Blockly.Gesture.prototype.startDraggingBubble_ = function() {
this.bubbleDragger_ = new Blockly.BubbleDragger(
/** @type {!Blockly.IBubble} */ (this.startBubble_),
/** @type {!Blockly.WorkspaceSvg} */ (this.startWorkspace_));
this.bubbleDragger_.startBubbleDrag();
this.bubbleDragger_.dragBubble(
this.mostRecentEvent_, this.currentDragDeltaXY_);
};
/**
* Start a gesture: update the workspace to indicate that a gesture is in
* progress and bind mousemove and mouseup handlers.
* @param {!Event} e A mouse down or touch start event.
* @package
*/
Blockly.Gesture.prototype.doStart = function(e) {
if (Blockly.utils.isTargetInput(e)) {
this.cancel();
return;
}
this.hasStarted_ = true;
Blockly.blockAnimations.disconnectUiStop();
this.startWorkspace_.updateScreenCalculationsIfScrolled();
if (this.startWorkspace_.isMutator) {
// Mutator's coordinate system could be out of date because the bubble was
// dragged, the block was moved, the parent workspace zoomed, etc.
this.startWorkspace_.resize();
}
// Hide chaff also hides the flyout, so don't do it if the click is in a
// flyout.
Blockly.hideChaff(!!this.flyout_);
this.startWorkspace_.markFocused();
this.mostRecentEvent_ = e;
Blockly.Tooltip.block();
if (this.targetBlock_) {
if (!this.targetBlock_.isInFlyout && e.shiftKey &&
this.targetBlock_.workspace.keyboardAccessibilityMode) {
this.creatorWorkspace_.getCursor().setCurNode(
Blockly.ASTNode.createTopNode(this.targetBlock_));
} else {
this.targetBlock_.select();
}
}
if (Blockly.utils.isRightButton(e)) {
this.handleRightClick(e);
return;
}
if ((e.type.toLowerCase() == 'touchstart' ||
e.type.toLowerCase() == 'pointerdown') &&
e.pointerType != 'mouse') {
Blockly.longStart(e, this);
}
this.mouseDownXY_ = new Blockly.utils.Coordinate(e.clientX, e.clientY);
this.healStack_ = e.altKey || e.ctrlKey || e.metaKey;
this.bindMouseEvents(e);
};
/**
* Bind gesture events.
* @param {!Event} e A mouse down or touch start event.
* @package
*/
Blockly.Gesture.prototype.bindMouseEvents = function(e) {
this.onMoveWrapper_ = Blockly.bindEventWithChecks_(
document, 'mousemove', null, this.handleMove.bind(this));
this.onUpWrapper_ = Blockly.bindEventWithChecks_(
document, 'mouseup', null, this.handleUp.bind(this));
e.preventDefault();
e.stopPropagation();
};
/**
* Handle a mouse move or touch move event.
* @param {!Event} e A mouse move or touch move event.
* @package
*/
Blockly.Gesture.prototype.handleMove = function(e) {
this.updateFromEvent_(e);
if (this.isDraggingWorkspace_) {
this.workspaceDragger_.drag(this.currentDragDeltaXY_);
} else if (this.isDraggingBlock_) {
this.blockDragger_.dragBlock(
this.mostRecentEvent_, this.currentDragDeltaXY_);
} else if (this.isDraggingBubble_) {
this.bubbleDragger_.dragBubble(
this.mostRecentEvent_, this.currentDragDeltaXY_);
}
e.preventDefault();
e.stopPropagation();
};
/**
* Handle a mouse up or touch end event.
* @param {!Event} e A mouse up or touch end event.
* @package
*/
Blockly.Gesture.prototype.handleUp = function(e) {
this.updateFromEvent_(e);
Blockly.longStop_();
if (this.isEnding_) {
console.log('Trying to end a gesture recursively.');
return;
}
this.isEnding_ = true;
// The ordering of these checks is important: drags have higher priority than
// clicks. Fields have higher priority than blocks; blocks have higher
// priority than workspaces.
// The ordering within drags does not matter, because the three types of
// dragging are exclusive.
if (this.isDraggingBubble_) {
this.bubbleDragger_.endBubbleDrag(e, this.currentDragDeltaXY_);
} else if (this.isDraggingBlock_) {
this.blockDragger_.endBlockDrag(e, this.currentDragDeltaXY_);
} else if (this.isDraggingWorkspace_) {
this.workspaceDragger_.endDrag(this.currentDragDeltaXY_);
} else if (this.isBubbleClick_()) {
// Bubbles are in front of all fields and blocks.
this.doBubbleClick_();
} else if (this.isFieldClick_()) {
this.doFieldClick_();
} else if (this.isBlockClick_()) {
this.doBlockClick_();
} else if (this.isWorkspaceClick_()) {
this.doWorkspaceClick_(e);
}
e.preventDefault();
e.stopPropagation();
this.dispose();
};
/**
* Cancel an in-progress gesture. If a workspace or block drag is in progress,
* end the drag at the most recent location.
* @package
*/
Blockly.Gesture.prototype.cancel = function() {
// Disposing of a block cancels in-progress drags, but dragging to a delete
// area disposes of a block and leads to recursive disposal. Break that cycle.
if (this.isEnding_) {
return;
}
Blockly.longStop_();
if (this.isDraggingBubble_) {
this.bubbleDragger_.endBubbleDrag(
this.mostRecentEvent_, this.currentDragDeltaXY_);
} else if (this.isDraggingBlock_) {
this.blockDragger_.endBlockDrag(
this.mostRecentEvent_, this.currentDragDeltaXY_);
} else if (this.isDraggingWorkspace_) {
this.workspaceDragger_.endDrag(this.currentDragDeltaXY_);
}
this.dispose();
};
/**
* Handle a real or faked right-click event by showing a context menu.
* @param {!Event} e A mouse move or touch move event.
* @package
*/
Blockly.Gesture.prototype.handleRightClick = function(e) {
if (this.targetBlock_) {
this.bringBlockToFront_();
Blockly.hideChaff(!!this.flyout_);
this.targetBlock_.showContextMenu(e);
} else if (this.startBubble_) {
this.startBubble_.showContextMenu(e);
} else if (this.startWorkspace_ && !this.flyout_) {
Blockly.hideChaff();
this.startWorkspace_.showContextMenu(e);
}
// TODO: Handle right-click on a bubble.
e.preventDefault();
e.stopPropagation();
this.dispose();
};
/**
* Handle a mousedown/touchstart event on a workspace.
* @param {!Event} e A mouse down or touch start event.
* @param {!Blockly.WorkspaceSvg} ws The workspace the event hit.
* @package
*/
Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.handleWsStart, ' +
'but the gesture had already been started.');
}
this.setStartWorkspace_(ws);
this.mostRecentEvent_ = e;
this.doStart(e);
if (this.startWorkspace_.keyboardAccessibilityMode) {
Blockly.navigation.setState(Blockly.navigation.STATE_WS);
}
};
/**
* Fires a workspace click event.
* @param {!Blockly.WorkspaceSvg} ws The workspace that a user clicks on.
* @private
*/
Blockly.Gesture.prototype.fireWorkspaceClick_ = function(ws) {
var clickEvent = new Blockly.Events.Ui(null, 'click', null, 'workspace');
clickEvent.workspaceId = ws.id;
Blockly.Events.fire(clickEvent);
};
/**
* Handle a mousedown/touchstart event on a flyout.
* @param {!Event} e A mouse down or touch start event.
* @param {!Blockly.IFlyout} flyout The flyout the event hit.
* @package
*/
Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.handleFlyoutStart, ' +
'but the gesture had already been started.');
}
this.setStartFlyout_(flyout);
this.handleWsStart(e, flyout.getWorkspace());
};
/**
* Handle a mousedown/touchstart event on a block.
* @param {!Event} e A mouse down or touch start event.
* @param {!Blockly.BlockSvg} block The block the event hit.
* @package
*/
Blockly.Gesture.prototype.handleBlockStart = function(e, block) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.handleBlockStart, ' +
'but the gesture had already been started.');
}
this.setStartBlock(block);
this.mostRecentEvent_ = e;
};
/**
* Handle a mousedown/touchstart event on a bubble.
* @param {!Event} e A mouse down or touch start event.
* @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, ' +
'but the gesture had already been started.');
}
this.setStartBubble(bubble);
this.mostRecentEvent_ = e;
};
/* Begin functions defining what actions to take to execute clicks on each type
* of target. Any developer wanting to add behaviour on clicks should modify
* only this code. */
/**
* Execute a bubble click.
* @private
*/
Blockly.Gesture.prototype.doBubbleClick_ = function() {
// TODO (#1673): Consistent handling of single clicks.
this.startBubble_.setFocus && this.startBubble_.setFocus();
this.startBubble_.select && this.startBubble_.select();
};
/**
* Execute a field click.
* @private
*/
Blockly.Gesture.prototype.doFieldClick_ = function() {
this.startField_.showEditor(this.mostRecentEvent_);
this.bringBlockToFront_();
};
/**
* Execute a block click.
* @private
*/
Blockly.Gesture.prototype.doBlockClick_ = function() {
// Block click in an autoclosing flyout.
if (this.flyout_ && this.flyout_.autoClose) {
if (this.targetBlock_.isEnabled()) {
if (!Blockly.Events.getGroup()) {
Blockly.Events.setGroup(true);
}
var newBlock = this.flyout_.createBlock(this.targetBlock_);
newBlock.scheduleSnapAndBump();
}
} else {
// Clicks events are on the start block, even if it was a shadow.
Blockly.Events.fire(
new Blockly.Events.Ui(this.startBlock_, 'click', undefined, 'block'));
}
this.bringBlockToFront_();
Blockly.Events.setGroup(false);
};
/**
* Execute a workspace click. When in accessibility mode shift clicking will
* move the cursor.
* @param {!Event} e A mouse up or touch end event.
* @private
*/
Blockly.Gesture.prototype.doWorkspaceClick_ = function(e) {
var ws = this.creatorWorkspace_;
if (e.shiftKey && ws.keyboardAccessibilityMode) {
var screenCoord = new Blockly.utils.Coordinate(e.clientX, e.clientY);
var wsCoord = Blockly.utils.screenToWsCoordinates(ws, screenCoord);
var wsNode = Blockly.ASTNode.createWorkspaceNode(ws, wsCoord);
ws.getCursor().setCurNode(wsNode);
} else if (Blockly.selected) {
Blockly.selected.unselect();
}
this.fireWorkspaceClick_(this.startWorkspace_ || ws);
};
/* End functions defining what actions to take to execute clicks on each type
* of target. */
// TODO (fenichel): Move bubbles to the front.
/**
* Move the dragged/clicked block to the front of the workspace so that it is
* not occluded by other blocks.
* @private
*/
Blockly.Gesture.prototype.bringBlockToFront_ = function() {
// Blocks in the flyout don't overlap, so skip the work.
if (this.targetBlock_ && !this.flyout_) {
this.targetBlock_.bringToFront();
}
};
/* Begin functions for populating a gesture at mouse down. */
/**
* Record the field that a gesture started on.
* @param {Blockly.Field} field The field the gesture started on.
* @package
*/
Blockly.Gesture.prototype.setStartField = function(field) {
if (this.hasStarted_) {
throw Error(
'Tried to call gesture.setStartField, ' +
'but the gesture had already been started.');
}
if (!this.startField_) {
this.startField_ = field;
}
};
/**
* Record the bubble that a gesture started on
* @param {Blockly.IBubble} bubble The bubble the gesture started on.
* @package
*/
Blockly.Gesture.prototype.setStartBubble = function(bubble) {
if (!this.startBubble_) {
this.startBubble_ = bubble;
}
};
/**
* Record the block that a gesture started on, and set the target block
* appropriately.
* @param {Blockly.BlockSvg} block The block the gesture started on.
* @package
*/
Blockly.Gesture.prototype.setStartBlock = function(block) {
// If the gesture already went through a bubble, don't set the start block.
if (!this.startBlock_ && !this.startBubble_) {
this.startBlock_ = block;
if (block.isInFlyout && block != block.getRootBlock()) {
this.setTargetBlock_(block.getRootBlock());
} else {
this.setTargetBlock_(block);
}
}
};
/**
* Record the block that a gesture targets, meaning the block that will be
* dragged if this turns into a drag. If this block is a shadow, that will be
* its first non-shadow parent.
* @param {Blockly.BlockSvg} block The block the gesture targets.
* @private
*/
Blockly.Gesture.prototype.setTargetBlock_ = function(block) {
if (block.isShadow()) {
this.setTargetBlock_(block.getParent());
} else {
this.targetBlock_ = block;
}
};
/**
* Record the workspace that a gesture started on.
* @param {Blockly.WorkspaceSvg} ws The workspace the gesture started on.
* @private
*/
Blockly.Gesture.prototype.setStartWorkspace_ = function(ws) {
if (!this.startWorkspace_) {
this.startWorkspace_ = ws;
}
};
/**
* Record the flyout that a gesture started on.
* @param {Blockly.IFlyout} flyout The flyout the gesture started on.
* @private
*/
Blockly.Gesture.prototype.setStartFlyout_ = function(flyout) {
if (!this.flyout_) {
this.flyout_ = flyout;
}
};
/* End functions for populating a gesture at mouse down. */
/* Begin helper functions defining types of clicks. Any developer wanting
* to change the definition of a click should modify only this code. */
/**
* Whether this gesture is a click on a bubble. This should only be called when
* ending a gesture (mouse up, touch end).
* @return {boolean} Whether this gesture was a click on a bubble.
* @private
*/
Blockly.Gesture.prototype.isBubbleClick_ = function() {
// A bubble click starts on a bubble and never escapes the drag radius.
var hasStartBubble = !!this.startBubble_;
return hasStartBubble && !this.hasExceededDragRadius_;
};
/**
* Whether this gesture is a click on a block. This should only be called when
* ending a gesture (mouse up, touch end).
* @return {boolean} Whether this gesture was a click on a block.
* @private
*/
Blockly.Gesture.prototype.isBlockClick_ = function() {
// A block click starts on a block, never escapes the drag radius, and is not
// a field click.
var hasStartBlock = !!this.startBlock_;
return hasStartBlock && !this.hasExceededDragRadius_ && !this.isFieldClick_();
};
/**
* Whether this gesture is a click on a field. This should only be called when
* ending a gesture (mouse up, touch end).
* @return {boolean} Whether this gesture was a click on a field.
* @private
*/
Blockly.Gesture.prototype.isFieldClick_ = function() {
var fieldClickable =
this.startField_ ? this.startField_.isClickable() : false;
return fieldClickable && !this.hasExceededDragRadius_ &&
(!this.flyout_ || !this.flyout_.autoClose);
};
/**
* Whether this gesture is a click on a workspace. This should only be called
* when ending a gesture (mouse up, touch end).
* @return {boolean} Whether this gesture was a click on a workspace.
* @private
*/
Blockly.Gesture.prototype.isWorkspaceClick_ = function() {
var onlyTouchedWorkspace =
!this.startBlock_ && !this.startBubble_ && !this.startField_;
return onlyTouchedWorkspace && !this.hasExceededDragRadius_;
};
/* End helper functions defining types of clicks. */
/**
* Whether this gesture is a drag of either a workspace or block.
* This function is called externally to block actions that cannot be taken
* mid-drag (e.g. using the keyboard to delete the selected blocks).
* @return {boolean} True if this gesture is a drag of a workspace or block.
* @package
*/
Blockly.Gesture.prototype.isDragging = function() {
return this.isDraggingWorkspace_ || this.isDraggingBlock_ ||
this.isDraggingBubble_;
};
/**
* Whether this gesture has already been started. In theory every mouse down
* has a corresponding mouse up, but in reality it is possible to lose a
* mouse up, leaving an in-process gesture hanging.
* @return {boolean} Whether this gesture was a click on a workspace.
* @package
*/
Blockly.Gesture.prototype.hasStarted = function() {
return this.hasStarted_;
};
/**
* Get a list of the insertion markers that currently exist. Block drags have
* 0, 1, or 2 insertion markers.
* @return {!Array.<!Blockly.BlockSvg>} A possibly empty list of insertion
* marker blocks.
* @package
*/
Blockly.Gesture.prototype.getInsertionMarkers = function() {
if (this.blockDragger_) {
return this.blockDragger_.getInsertionMarkers();
}
return [];
};
/**
* Is a drag or other gesture currently in progress on any workspace?
* @return {boolean} True if gesture is occurring.
*/
Blockly.Gesture.inProgress = function() {
var workspaces = Blockly.Workspace.getAll();
for (var i = 0, workspace; (workspace = workspaces[i]); i++) {
if (workspace.currentGesture_) {
return true;
}
}
return false;
};