diff --git a/core/block_svg.js b/core/block_svg.js index 3e0ec7a68..7dd81bfc9 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -292,7 +292,6 @@ Blockly.BlockSvg.terminateDrag = function() { } } Blockly.dragMode_ = Blockly.DRAG_NONE; - Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN); }; /** @@ -653,8 +652,6 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) { Blockly.Events.setGroup(true); } // Left-click (or middle click) - Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); - this.dragStartXY_ = this.getRelativeToSurfaceXY(); this.workspace.startDrag(e, this.dragStartXY_); @@ -691,6 +688,11 @@ Blockly.BlockSvg.prototype.onMouseUp_ = function(e) { Blockly.Touch.clearTouchIdentifier(); if (Blockly.dragMode_ != Blockly.DRAG_FREE && !Blockly.WidgetDiv.isVisible()) { + // Move the block in front of the others. Do this at the end of a click + // instead of rearranging the dom on mousedown. This helps with + // performance and makes it easier to use psuedo element :active + // to set the cursor. + this.bringToFront_(); Blockly.Events.fire( new Blockly.Events.Ui(this, 'click', undefined, undefined)); } @@ -721,13 +723,16 @@ Blockly.BlockSvg.prototype.onMouseUp_ = function(e) { if (trashcan) { goog.Timer.callOnce(trashcan.close, 100, trashcan); } + if (this.workspace.toolbox_) { + this.workspace.toolbox_.removeDeleteStyle(); + } + Blockly.selected.dispose(false, true); } if (Blockly.highlightedConnection_) { Blockly.highlightedConnection_.unhighlight(); Blockly.highlightedConnection_ = null; } - Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN); if (!Blockly.WidgetDiv.isVisible()) { Blockly.Events.setGroup(false); } @@ -1063,16 +1068,28 @@ Blockly.BlockSvg.prototype.updateCursor_ = function(e, closestConnection) { var showDeleteCursor = wouldDelete && !wouldConnect; if (showDeleteCursor) { - Blockly.Css.setCursor(Blockly.Css.Cursor.DELETE); + Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), + 'blocklyDraggingDelete'); + + if (this.workspace.toolbox_) { + // Change the cursor to a hand with an 'x' + this.workspace.toolbox_.addDeleteStyle(); + } + if (deleteArea == Blockly.DELETE_AREA_TRASH && this.workspace.trashcan) { this.workspace.trashcan.setOpen_(true); } return true; } else { - Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); if (this.workspace.trashcan) { this.workspace.trashcan.setOpen_(false); } + Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), + 'blocklyDraggingDelete'); + if (this.workspace.toolbox_) { + // Change the cursor on the toolbox + this.workspace.toolbox_.removeDeleteStyle(); + } return false; } }; @@ -1587,13 +1604,6 @@ Blockly.BlockSvg.prototype.setHighlighted = function(highlighted) { Blockly.BlockSvg.prototype.addSelect = function() { Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), 'blocklySelected'); - // Move the selected block to the top of the stack. - var block = this; - do { - var root = block.getSvgRoot(); - root.parentNode.appendChild(root); - block = block.getParent(); - } while (block); }; /** @@ -1619,6 +1629,21 @@ Blockly.BlockSvg.prototype.setColour = function(colour) { } }; + +/** + * Move this block to the front of the visible workspace. + * tags do not respect z-index so svg renders them in the + * order that they are in the dom. By placing this block first within the + * block group's , it will render on top of any other blocks. + */ +Blockly.BlockSvg.prototype.bringToFront_ = function() { + var block = this; + do { + var root = block.getSvgRoot(); + root.parentNode.appendChild(root); + block = block.getParent(); + } while (block); +}; /** * Set whether this block can chain onto the bottom of another block. * @param {boolean} newBoolean True if there can be a previous statement. diff --git a/core/bubble.js b/core/bubble.js index 143ba25c6..1cd9443fd 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -152,7 +152,6 @@ Blockly.Bubble.unbindDragEvents_ = function() { */ Blockly.Bubble.bubbleMouseUp_ = function(/*e*/) { Blockly.Touch.clearTouchIdentifier(); - Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN); Blockly.Bubble.unbindDragEvents_(); }; @@ -280,8 +279,6 @@ Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) { return; } // Left-click (or middle click) - Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); - this.workspace_.startDrag(e, new goog.math.Coordinate( this.workspace_.RTL ? -this.relativeLeft_ : this.relativeLeft_, this.relativeTop_)); @@ -323,8 +320,6 @@ Blockly.Bubble.prototype.resizeMouseDown_ = function(e) { return; } // Left-click (or middle click) - Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); - this.workspace_.startDrag(e, new goog.math.Coordinate( this.workspace_.RTL ? -this.width_ : this.width_, this.height_)); diff --git a/core/css.js b/core/css.js index 1e630bfd3..8ff22ad8f 100644 --- a/core/css.js +++ b/core/css.js @@ -95,41 +95,17 @@ Blockly.Css.inject = function(hasCss, pathToMedia) { var cssTextNode = document.createTextNode(text); cssNode.appendChild(cssTextNode); Blockly.Css.styleSheet_ = cssNode.sheet; - Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN); }; /** * Set the cursor to be displayed when over something draggable. + * See See https://github.com/google/blockly/issues/981 for context. * @param {Blockly.Css.Cursor} cursor Enum. + * @deprecated April 2017. */ Blockly.Css.setCursor = function(cursor) { - if (Blockly.Css.currentCursor_ == cursor) { - return; - } - Blockly.Css.currentCursor_ = cursor; - var url = 'url(' + Blockly.Css.mediaPath_ + '/' + cursor + '.cur), auto'; - // There are potentially hundreds of draggable objects. Changing their style - // properties individually is too slow, so change the CSS rule instead. - var rule = '.blocklyDraggable {\n cursor: ' + url + ';\n}\n'; - Blockly.Css.styleSheet_.deleteRule(0); - Blockly.Css.styleSheet_.insertRule(rule, 0); - // There is probably only one toolbox, so just change its style property. - var toolboxen = document.getElementsByClassName('blocklyToolboxDiv'); - for (var i = 0, toolbox; toolbox = toolboxen[i]; i++) { - if (cursor == Blockly.Css.Cursor.DELETE) { - toolbox.style.cursor = url; - } else { - toolbox.style.cursor = ''; - } - } - // Set cursor on the whole document, so that rapid movements - // don't result in cursor changing to an arrow momentarily. - var html = document.body.parentNode; - if (cursor == Blockly.Css.Cursor.OPEN) { - html.style.cursor = ''; - } else { - html.style.cursor = url; - } + console.warn('Deprecated call to Blockly.Css.setCursor.' + + 'See https://github.com/google/blockly/issues/981 for context'); }; /** @@ -232,6 +208,49 @@ Blockly.Css.CONTENT = [ 'display: none;', '}', + '.blocklyDraggable {', + /* backup for browsers (e.g. IE11) that don't support grab */ + 'cursor: url("<<>>/handopen.cur"), auto;', + 'cursor: grab;', + 'cursor: -webkit-grab;', + 'cursor: -moz-grab;', + '}', + + '.blocklyDragging {', + /* backup for browsers (e.g. IE11) that don't support grabbing */ + 'cursor: url("<<>>/handclosed.cur"), auto;', + 'cursor: grabbing;', + 'cursor: -webkit-grabbing;', + 'cursor: -moz-grabbing;', + '}', + /* Changes cursor on mouse down. Not effective in Firefox because of + https://bugzilla.mozilla.org/show_bug.cgi?id=771241 */ + '.blocklyDraggable:active {', + /* backup for browsers (e.g. IE11) that don't support grabbing */ + 'cursor: url("<<>>/handclosed.cur"), auto;', + 'cursor: grabbing;', + 'cursor: -webkit-grabbing;', + 'cursor: -moz-grabbing;', + '}', + /* Change the cursor on the whole drag surface in case the mouse gets + ahead of block during a drag. This way the cursor is still a closed hand. + */ + '.blocklyBlockDragSurface .blocklyDraggable {', + /* backup for browsers (e.g. IE11) that don't support grabbing */ + 'cursor: url("<<>>/handclosed.cur"), auto;', + 'cursor: grabbing;', + 'cursor: -webkit-grabbing;', + 'cursor: -moz-grabbing;', + '}', + + '.blocklyDragging.blocklyDraggingDelete {', + 'cursor: url("<<>>/handdelete.cur"), auto;', + '}', + + '.blocklyToolboxDelete {', + 'cursor: url("<<>>/handdelete.cur"), auto;', + '}', + '.blocklyDragging>.blocklyPath,', '.blocklyDragging>.blocklyPathLight {', 'fill-opacity: .8;', diff --git a/core/flyout.js b/core/flyout.js index 6bf4ac0e4..e7133c317 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -960,7 +960,6 @@ Blockly.Flyout.prototype.blockMouseDown_ = function(block) { Blockly.terminateDrag_(); Blockly.hideChaff(true); // Left-click (or middle click) - Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED); // Record the current mouse position. flyout.startDragMouseY_ = e.clientY; flyout.startDragMouseX_ = e.clientX; diff --git a/core/mutator.js b/core/mutator.js index 641273e6f..30486227b 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -137,7 +137,11 @@ Blockly.Mutator.prototype.createEditor_ = function() { // To fix this, scale needs to be applied at a different level in the dom. var flyoutSvg = this.workspace_.addFlyout_('g'); var background = this.workspace_.createDom('blocklyMutatorBackground'); - background.appendChild(flyoutSvg); + + // Insert the flyout after the but before the block canvas so that + // the flyout is underneath in z-order. This makes blocks layering during + // dragging work properly. + background.insertBefore(flyoutSvg, this.workspace_.svgBlockCanvas_); this.svgDialog_.appendChild(background); return this.svgDialog_; diff --git a/core/toolbox.js b/core/toolbox.js index 755298530..d36655c52 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -413,6 +413,24 @@ Blockly.Toolbox.prototype.clearSelection = function() { this.tree_.setSelectedItem(null); }; +/** + * Adds styles on the toolbox indicating blocks will be deleted. + * @package + */ +Blockly.Toolbox.prototype.addDeleteStyle = function() { + Blockly.utils.addClass(/** @type {!Element} */ (this.HtmlDiv), + 'blocklyToolboxDelete'); +}; + +/** + * Remove styles from the toolbox that indicate blocks will be deleted. + * @package + */ +Blockly.Toolbox.prototype.removeDeleteStyle = function() { + Blockly.utils.removeClass(/** @type {!Element} */ (this.HtmlDiv), + 'blocklyToolboxDelete'); +}; + /** * Return the deletion rectangle for this toolbox. * @return {goog.math.Rect} Rectangle in which to delete. diff --git a/core/touch.js b/core/touch.js index 566bd3665..303e4c50b 100644 --- a/core/touch.js +++ b/core/touch.js @@ -121,7 +121,6 @@ Blockly.onMouseUp_ = function(e) { // TODO(#781): Check whether this needs to be called for all drag modes. workspace.resetDragSurface(); - Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN); workspace.dragMode_ = Blockly.DRAG_NONE; // Unbind the touch event if it exists. if (Blockly.Touch.onTouchUpWrapper_) {