diff --git a/core/components/menu/menu.js b/core/components/menu/menu.js index ab89585db..40f517469 100644 --- a/core/components/menu/menu.js +++ b/core/components/menu/menu.js @@ -25,6 +25,7 @@ goog.provide('Blockly.Menu'); goog.require('Blockly.Component'); goog.require('Blockly.utils.aria'); +goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); @@ -37,6 +38,15 @@ goog.require('Blockly.utils.object'); Blockly.Menu = function() { Blockly.Component.call(this); + /** + * Coordinates of the mousedown event that caused this menu to open. Used to + * prevent the consequent mouseup event due to a simple click from activating + * a menu item immediately. + * @type {?Blockly.utils.Coordinate} + * @package + */ + this.openingCoords = null; + /** * This is the element that we will listen to the real focus events on. * A value of -1 means no menuitem is highlighted. @@ -432,6 +442,20 @@ Blockly.Menu.prototype.handleMouseOver_ = function(e) { * @private */ Blockly.Menu.prototype.handleClick_ = function(e) { + var oldCoords = this.openingCoords; + // Clear out the saved opening coords immediately so they're not used twice. + this.openingCoords = null; + if (oldCoords && typeof e.clientX === 'number') { + var newCoords = new Blockly.utils.Coordinate(e.clientX, e.clientY); + if (Blockly.utils.Coordinate.distance(oldCoords, newCoords) < 1) { + // This menu was opened by a mousedown and we're handling the consequent + // click event. The coords haven't changed, meaning this was the same + // opening event. Don't do the usual behavior because the menu just popped + // up under the mouse and the user didn't mean to activate this item. + return; + } + } + var menuItem = this.getMenuItem(/** @type {Node} */ (e.target)); if (menuItem && menuItem.handleClick(e)) { diff --git a/core/field.js b/core/field.js index ae7d1cdd7..09507898d 100644 --- a/core/field.js +++ b/core/field.js @@ -204,6 +204,8 @@ Blockly.Field.prototype.getText_; * An optional method that can be defined to show an editor when the field is * clicked. Blockly will automatically set the field as clickable if this * method is defined. + * @param {Event=} opt_e Optional mouse event that triggered the field to open, + * or undefined if triggered programatically. * @return {void} * @protected */ @@ -602,11 +604,13 @@ Blockly.Field.prototype.render_ = function() { /** * Show an editor when the field is clicked only if the field is clickable. + * @param {Event=} opt_e Optional mouse event that triggered the field to open, + * or undefined if triggered programatically. * @package */ -Blockly.Field.prototype.showEditor = function() { +Blockly.Field.prototype.showEditor = function(opt_e) { if (this.isClickable()) { - this.showEditor_(); + this.showEditor_(opt_e); } }; diff --git a/core/field_angle.js b/core/field_angle.js index 5b8b75bc3..131adce28 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -256,15 +256,17 @@ Blockly.FieldAngle.prototype.render_ = function() { /** * Create and show the angle field's editor. + * @param {Event=} opt_e Optional mouse event that triggered the field to open, + * or undefined if triggered programatically. * @private */ -Blockly.FieldAngle.prototype.showEditor_ = function() { +Blockly.FieldAngle.prototype.showEditor_ = function(opt_e) { // Mobile browsers have issues with in-line textareas (focus & keyboards). var noFocus = Blockly.utils.userAgent.MOBILE || Blockly.utils.userAgent.ANDROID || Blockly.utils.userAgent.IPAD; - Blockly.FieldAngle.superClass_.showEditor_.call(this, noFocus); + Blockly.FieldAngle.superClass_.showEditor_.call(this, opt_e, noFocus); var editor = this.dropdownCreate_(); Blockly.DropDownDiv.getContentDiv().appendChild(editor); diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 42232dbb1..0e130460d 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -34,6 +34,7 @@ goog.require('Blockly.MenuItem'); goog.require('Blockly.navigation'); goog.require('Blockly.utils'); goog.require('Blockly.utils.aria'); +goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Size'); @@ -267,10 +268,18 @@ Blockly.FieldDropdown.prototype.createSVGArrow_ = function() { /** * Create a dropdown menu under the text. + * @param {Event=} opt_e Optional mouse event that triggered the field to open, + * or undefined if triggered programatically. * @private */ -Blockly.FieldDropdown.prototype.showEditor_ = function() { +Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) { this.menu_ = this.dropdownCreate_(); + if (opt_e && typeof opt_e.clientX === 'number') { + this.menu_.openingCoords = + new Blockly.utils.Coordinate(opt_e.clientX, opt_e.clientY); + } else { + this.menu_.openingCoords = null; + } // Element gets created in render. this.menu_.render(Blockly.DropDownDiv.getContentDiv()); Blockly.utils.dom.addClass( diff --git a/core/field_textinput.js b/core/field_textinput.js index aa6d0a7a6..34a5a0e9a 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -285,11 +285,14 @@ Blockly.FieldTextInput.prototype.setSpellcheck = function(check) { /** * Show the inline free-text editor on top of the text. + * @param {Event=} _opt_e Optional mouse event that triggered the field to open, + * or undefined if triggered programatically. * @param {boolean=} opt_quietInput True if editor should be created without * focus. Defaults to false. * @protected */ -Blockly.FieldTextInput.prototype.showEditor_ = function(opt_quietInput) { +Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e, + opt_quietInput) { this.workspace_ = this.sourceBlock_.workspace; var quietInput = opt_quietInput || false; if (!quietInput && (Blockly.utils.userAgent.MOBILE || diff --git a/core/gesture.js b/core/gesture.js index 76070a0b3..1e069ec4e 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -731,7 +731,7 @@ Blockly.Gesture.prototype.doBubbleClick_ = function() { * @private */ Blockly.Gesture.prototype.doFieldClick_ = function() { - this.startField_.showEditor(); + this.startField_.showEditor(this.mostRecentEvent_); this.bringBlockToFront_(); };