mirror of
https://github.com/google/blockly.git
synced 2026-01-10 02:17:09 +01:00
Add red 'X' to mouse cursor if blocks are over flyout.
This commit is contained in:
@@ -300,6 +300,7 @@ Blockly.Block.terminateDrag_ = function() {
|
||||
selected.workspace.fireChangeEvent();
|
||||
}
|
||||
Blockly.Block.dragMode_ = 0;
|
||||
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -557,7 +558,6 @@ Blockly.Block.prototype.onMouseDown_ = function(e) {
|
||||
Blockly.Block.prototype.onMouseUp_ = function(e) {
|
||||
var this_ = this;
|
||||
Blockly.doCommand(function() {
|
||||
Blockly.terminateDrag_();
|
||||
if (Blockly.selected && Blockly.highlightedConnection_) {
|
||||
// Connect two blocks together.
|
||||
Blockly.localConnection_.connect(Blockly.highlightedConnection_);
|
||||
@@ -572,13 +572,15 @@ Blockly.Block.prototype.onMouseUp_ = function(e) {
|
||||
}
|
||||
inferiorConnection.sourceBlock_.svg_.connectionUiEffect();
|
||||
}
|
||||
if (this_.workspace.trashcan && this_.workspace.trashcan.isOpen) {
|
||||
if (this_.workspace.trashcan) {
|
||||
// Don't throw an object in the trash can if it just got connected.
|
||||
this_.workspace.trashcan.close();
|
||||
}
|
||||
} else if (this_.workspace.trashcan && this_.workspace.trashcan.isOpen) {
|
||||
} else if (this_.workspace.isDeleteArea(e)) {
|
||||
var trashcan = this_.workspace.trashcan;
|
||||
goog.Timer.callOnce(trashcan.close, 100, trashcan);
|
||||
if (trashcan) {
|
||||
goog.Timer.callOnce(trashcan.close, 100, trashcan);
|
||||
}
|
||||
Blockly.selected.dispose(false, true);
|
||||
// Dropping a block on the trash can will usually cause the workspace to
|
||||
// resize to contain the newly positioned block. Force a second resize
|
||||
@@ -589,6 +591,7 @@ Blockly.Block.prototype.onMouseUp_ = function(e) {
|
||||
Blockly.highlightedConnection_.unhighlight();
|
||||
Blockly.highlightedConnection_ = null;
|
||||
}
|
||||
Blockly.terminateDrag_();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -862,6 +865,7 @@ Blockly.Block.prototype.onMouseMove_ = function(e) {
|
||||
// Push this block to the very top of the stack.
|
||||
this_.setParent(null);
|
||||
this_.setDragging_(true);
|
||||
this_.workspace.recordDeleteAreas();
|
||||
}
|
||||
}
|
||||
if (Blockly.Block.dragMode_ == 2) {
|
||||
@@ -907,9 +911,10 @@ Blockly.Block.prototype.onMouseMove_ = function(e) {
|
||||
Blockly.highlightedConnection_ = closestConnection;
|
||||
Blockly.localConnection_ = localConnection;
|
||||
}
|
||||
// Flip the trash can lid if needed.
|
||||
if (this_.workspace.trashcan && this_.isDeletable()) {
|
||||
this_.workspace.trashcan.onMouseMove(e);
|
||||
// Provide visual indication of whether the block will be deleted if
|
||||
// dropped here.
|
||||
if (this_.isDeletable()) {
|
||||
this_.workspace.isDeleteArea(e);
|
||||
}
|
||||
}
|
||||
// This event has been handled. No need to bubble up to the document.
|
||||
|
||||
25
core/css.js
25
core/css.js
@@ -28,6 +28,7 @@ goog.provide('Blockly.Css');
|
||||
|
||||
goog.require('goog.cssom');
|
||||
|
||||
|
||||
/**
|
||||
* List of cursors.
|
||||
* @enum {string}
|
||||
@@ -38,6 +39,12 @@ Blockly.Css.Cursor = {
|
||||
DELETE: 'handdelete'
|
||||
};
|
||||
|
||||
/**
|
||||
* Current cursor (cached value).
|
||||
* @type string
|
||||
*/
|
||||
Blockly.Css.currentCursor_ = '';
|
||||
|
||||
/**
|
||||
* Large stylesheet added by Blockly.Css.inject.
|
||||
* @type Element
|
||||
@@ -75,9 +82,10 @@ Blockly.Css.inject = function() {
|
||||
* @param {Blockly.Cursor} cursor Enum.
|
||||
*/
|
||||
Blockly.Css.setCursor = function(cursor) {
|
||||
if (Blockly.readOnly) {
|
||||
if (Blockly.readOnly || Blockly.Css.currentCursor_ == cursor) {
|
||||
return;
|
||||
}
|
||||
Blockly.Css.currentCursor_ = cursor;
|
||||
/*
|
||||
Hotspot coordinates are baked into the CUR file, but they are still
|
||||
required in the CSS due to a Chrome bug.
|
||||
@@ -88,10 +96,19 @@ Blockly.Css.setCursor = function(cursor) {
|
||||
} else {
|
||||
var xy = '7 3';
|
||||
}
|
||||
var rule = '.blocklyDraggable {\n' +
|
||||
' cursor: url(' + Blockly.Css.mediaPath_ + '/' + cursor + '.cur)' +
|
||||
' ' + xy + ', auto;\n}\n';
|
||||
var url = 'url(' + Blockly.Css.mediaPath_ + '/' + cursor +
|
||||
'.cur) ' + xy + ', auto';
|
||||
var rule = '.blocklyDraggable {\n cursor: ' + url + ';\n}\n';
|
||||
goog.cssom.replaceCssRule('', rule, Blockly.Css.styleSheet_, 0);
|
||||
if (Blockly.svg) {
|
||||
// Set cursor on the SVG surface as well, so that rapid movements
|
||||
// don't result in cursor changing to an arrow momentarily.
|
||||
if (cursor == Blockly.Css.Cursor.OPEN) {
|
||||
Blockly.svg.style.cursor = '';
|
||||
} else {
|
||||
Blockly.svg.style.cursor = url;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ goog.provide('Blockly.Flyout');
|
||||
goog.require('Blockly.Block');
|
||||
goog.require('Blockly.Comment');
|
||||
goog.require('goog.userAgent');
|
||||
goog.require('goog.math.Rect');
|
||||
|
||||
|
||||
/**
|
||||
@@ -670,3 +671,20 @@ Blockly.Flyout.terminateDrag_ = function() {
|
||||
Blockly.Flyout.startBlock_ = null;
|
||||
Blockly.Flyout.startFlyout_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the deletion rectangle for this flyout.
|
||||
* @return {goog.math.Rect} Rectangle in which to delete.
|
||||
*/
|
||||
Blockly.Flyout.prototype.getRect = function() {
|
||||
// BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
|
||||
// area are still deleted. Must be smaller than Infinity, but larger than
|
||||
// the largest screen size.
|
||||
var BIG_NUM = 10000000;
|
||||
var x = Blockly.getSvgXY_(this.svgGroup_).x;
|
||||
if (!Blockly.RTL) {
|
||||
x -= BIG_NUM;
|
||||
}
|
||||
return new goog.math.Rect(x, -BIG_NUM,
|
||||
BIG_NUM + this.width_, this.height_ + 2 * BIG_NUM);
|
||||
};
|
||||
|
||||
@@ -327,11 +327,6 @@ Blockly.createDom_ = function(container) {
|
||||
if (overflow < 0) {
|
||||
block.moveBy(overflow, 0);
|
||||
}
|
||||
// Delete any block that's sitting on top of the flyout.
|
||||
if (block.isDeletable() && (Blockly.RTL ?
|
||||
blockXY.x - metrics.viewWidth : -blockXY.x) > MARGIN * 2) {
|
||||
block.dispose(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ Blockly.Mutator.prototype.createEditor_ = function() {
|
||||
var mutator = this;
|
||||
this.workspace_ = new Blockly.Workspace(
|
||||
function() {return mutator.getFlyoutMetrics_();}, null);
|
||||
this.flyout_ = new Blockly.Flyout();
|
||||
this.flyout_.autoClose = false;
|
||||
this.svgDialog_.appendChild(this.flyout_.createDom());
|
||||
this.workspace_.flyout_ = new Blockly.Flyout();
|
||||
this.workspace_.flyout_.autoClose = false;
|
||||
this.svgDialog_.appendChild(this.workspace_.flyout_.createDom());
|
||||
this.svgDialog_.appendChild(this.workspace_.createDom());
|
||||
return this.svgDialog_;
|
||||
};
|
||||
@@ -148,7 +148,7 @@ Blockly.Mutator.prototype.updateEditable = function() {
|
||||
Blockly.Mutator.prototype.resizeBubble_ = function() {
|
||||
var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH;
|
||||
var workspaceSize = this.workspace_.getCanvas().getBBox();
|
||||
var flyoutMetrics = this.flyout_.getMetrics_();
|
||||
var flyoutMetrics = this.workspace_.flyout_.getMetrics_();
|
||||
var width;
|
||||
if (Blockly.RTL) {
|
||||
width = -workspaceSize.x;
|
||||
@@ -193,8 +193,8 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
|
||||
this.createEditor_(), this.block_.svg_.svgPath_,
|
||||
this.iconX_, this.iconY_, null, null);
|
||||
var thisObj = this;
|
||||
this.flyout_.init(this.workspace_);
|
||||
this.flyout_.show(this.quarkXml_);
|
||||
this.workspace_.flyout_.init(this.workspace_);
|
||||
this.workspace_.flyout_.show(this.quarkXml_);
|
||||
|
||||
this.rootBlock_ = this.block_.decompose(this.workspace_);
|
||||
var blocks = this.rootBlock_.getDescendants();
|
||||
@@ -204,8 +204,8 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
|
||||
// The root block should not be dragable or deletable.
|
||||
this.rootBlock_.setMovable(false);
|
||||
this.rootBlock_.setDeletable(false);
|
||||
var margin = this.flyout_.CORNER_RADIUS * 2;
|
||||
var x = this.flyout_.width_ + margin;
|
||||
var margin = this.workspace_.flyout_.CORNER_RADIUS * 2;
|
||||
var x = this.workspace_.flyout_.width_ + margin;
|
||||
if (Blockly.RTL) {
|
||||
x = -x;
|
||||
}
|
||||
@@ -226,8 +226,6 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
|
||||
} else {
|
||||
// Dispose of the bubble.
|
||||
this.svgDialog_ = null;
|
||||
this.flyout_.dispose();
|
||||
this.flyout_ = null;
|
||||
this.workspace_.dispose();
|
||||
this.workspace_ = null;
|
||||
this.rootBlock_ = null;
|
||||
@@ -244,7 +242,7 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
|
||||
|
||||
/**
|
||||
* Update the source block when the mutator's blocks are changed.
|
||||
* Delete or bump any block that's out of bounds.
|
||||
* Bump down any block that's too high.
|
||||
* Fired whenever a change is made to the mutator's workspace.
|
||||
* @private
|
||||
*/
|
||||
@@ -255,12 +253,7 @@ Blockly.Mutator.prototype.workspaceChanged_ = function() {
|
||||
for (var b = 0, block; block = blocks[b]; b++) {
|
||||
var blockXY = block.getRelativeToSurfaceXY();
|
||||
var blockHW = block.getHeightWidth();
|
||||
if (block.isDeletable() && (Blockly.RTL ?
|
||||
blockXY.x > -this.flyout_.width_ + MARGIN :
|
||||
blockXY.x < this.flyout_.width_ - MARGIN)) {
|
||||
// Delete any block that's sitting on top of the flyout.
|
||||
block.dispose(false, true);
|
||||
} else if (blockXY.y + blockHW.height < MARGIN) {
|
||||
if (blockXY.y + blockHW.height < MARGIN) {
|
||||
// Bump any block that's above the top back inside.
|
||||
block.moveBy(0, MARGIN - blockHW.height - blockXY.y);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
goog.provide('Blockly.Trashcan');
|
||||
|
||||
goog.require('goog.math');
|
||||
goog.require('goog.math.Rect');
|
||||
goog.require('goog.Timer');
|
||||
|
||||
|
||||
@@ -238,32 +239,16 @@ Blockly.Trashcan.prototype.position_ = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if the mouse is currently over the trash can.
|
||||
* Opens/closes the lid and sets the isOpen flag.
|
||||
* @param {!Event} e Mouse move event.
|
||||
* Return the deletion rectangle for this trashcan.
|
||||
* @return {goog.math.Rect} Rectangle in which to delete.
|
||||
*/
|
||||
Blockly.Trashcan.prototype.onMouseMove = function(e) {
|
||||
/*
|
||||
An alternative approach would be to use onMouseOver and onMouseOut events.
|
||||
However the selected block will be between the mouse and the trash can,
|
||||
thus these events won't fire.
|
||||
Another approach is to use HTML5's drag & drop API, but it's widely hated.
|
||||
Instead, we'll just have the block's drag_ function call us.
|
||||
*/
|
||||
if (!this.svgGroup_) {
|
||||
return;
|
||||
}
|
||||
var mouseXY = Blockly.mouseToSvg(e);
|
||||
Blockly.Trashcan.prototype.getRect = function() {
|
||||
var trashXY = Blockly.getSvgXY_(this.svgGroup_);
|
||||
var over = (mouseXY.x > trashXY.x - this.MARGIN_HOTSPOT_) &&
|
||||
(mouseXY.x < trashXY.x + this.WIDTH_ + this.MARGIN_HOTSPOT_) &&
|
||||
(mouseXY.y > trashXY.y - this.MARGIN_HOTSPOT_) &&
|
||||
(mouseXY.y < trashXY.y + this.BODY_HEIGHT_ + this.LID_HEIGHT_ +
|
||||
this.MARGIN_HOTSPOT_);
|
||||
// For bonus points we might want to match the trapezoidal outline.
|
||||
if (this.isOpen != over) {
|
||||
this.setOpen_(over);
|
||||
}
|
||||
return new goog.math.Rect(
|
||||
trashXY.x - this.MARGIN_HOTSPOT_,
|
||||
trashXY.y - this.MARGIN_HOTSPOT_,
|
||||
this.WIDTH_ + 2 * this.MARGIN_HOTSPOT_,
|
||||
this.BODY_HEIGHT_ + this.LID_HEIGHT_ + 2 * this.MARGIN_HOTSPOT_);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -278,8 +263,6 @@ Blockly.Trashcan.prototype.setOpen_ = function(state) {
|
||||
goog.Timer.clear(this.lidTask_);
|
||||
this.isOpen = state;
|
||||
this.animateLid_();
|
||||
Blockly.Css.setCursor(state ? Blockly.Css.Cursor.DELETE :
|
||||
Blockly.Css.Cursor.CLOSED);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,7 @@ goog.require('Blockly.ScrollbarPair');
|
||||
goog.require('Blockly.Trashcan');
|
||||
goog.require('Blockly.Xml');
|
||||
goog.require('goog.math');
|
||||
goog.require('goog.math.Coordinate');
|
||||
|
||||
|
||||
/**
|
||||
@@ -134,6 +135,10 @@ Blockly.Workspace.prototype.dispose = function() {
|
||||
}
|
||||
this.svgBlockCanvas_ = null;
|
||||
this.svgBubbleCanvas_ = null;
|
||||
if (this.flyout_) {
|
||||
this.flyout_.dispose();
|
||||
this.flyout_ = null;
|
||||
}
|
||||
if (this.trashcan) {
|
||||
this.trashcan.dispose();
|
||||
this.trashcan = null;
|
||||
@@ -396,5 +401,49 @@ Blockly.Workspace.prototype.remainingCapacity = function() {
|
||||
return this.maxBlocks - this.getAllBlocks().length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a list of all the delete areas for this workspace.
|
||||
*/
|
||||
Blockly.Workspace.prototype.recordDeleteAreas = function() {
|
||||
if (this.trashcan) {
|
||||
this.deleteAreaTrash_ = this.trashcan.getRect();
|
||||
} else {
|
||||
this.deleteAreaTrash_ = null;
|
||||
}
|
||||
if (this.flyout_) {
|
||||
this.deleteAreaToolbox_ = this.flyout_.getRect();
|
||||
} else {
|
||||
this.deleteAreaToolbox_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the mouse event over a delete area (toolbar or non-closing flyout)?
|
||||
* Opens or closes the trashcan and sets the cursor as a side effect.
|
||||
* @param {!Event} e Mouse move event.
|
||||
* @return {boolean} True if event is in a delete area.
|
||||
*/
|
||||
Blockly.Workspace.prototype.isDeleteArea = function(e) {
|
||||
var isDelete = false;
|
||||
var mouseXY = Blockly.mouseToSvg(e);
|
||||
var xy = new goog.math.Coordinate(mouseXY.x, mouseXY.y);
|
||||
if (this.deleteAreaTrash_) {
|
||||
if (this.deleteAreaTrash_.contains(xy)) {
|
||||
this.trashcan.setOpen_(true);
|
||||
Blockly.Css.setCursor(Blockly.Css.Cursor.DELETE);
|
||||
return true;
|
||||
}
|
||||
this.trashcan.setOpen_(false);
|
||||
}
|
||||
if (this.deleteAreaToolbox_) {
|
||||
if (this.deleteAreaToolbox_.contains(xy)) {
|
||||
Blockly.Css.setCursor(Blockly.Css.Cursor.DELETE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);
|
||||
return false;
|
||||
};
|
||||
|
||||
// Export symbols that would otherwise be renamed by Closure compiler.
|
||||
Blockly.Workspace.prototype['clear'] = Blockly.Workspace.prototype.clear;
|
||||
|
||||
Reference in New Issue
Block a user