mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-06-06 01:44:35 -04:00
322 lines
8.5 KiB
JavaScript
322 lines
8.5 KiB
JavaScript
/**
|
|
* @license
|
|
* Visual Blocks Editor
|
|
*
|
|
* Copyright 2016 Google Inc.
|
|
* https://developers.google.com/blockly/
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Class for a button in the flyout.
|
|
* @author fenichel@google.com (Rachel Fenichel)
|
|
*/
|
|
'use strict';
|
|
|
|
goog.provide('Blockly.FlyoutButton');
|
|
|
|
goog.require('goog.dom');
|
|
goog.require('goog.math.Coordinate');
|
|
|
|
|
|
/**
|
|
* Class for a button or label in the flyout. Labels behave the same as buttons,
|
|
* but are styled differently.
|
|
* @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this
|
|
* button.
|
|
* @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace.
|
|
* @param {!Element} xml The XML specifying the label/button.
|
|
* @param {boolean} isLabel Whether this button should be styled as a label.
|
|
* @constructor
|
|
*/
|
|
Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) {
|
|
|
|
this.init(workspace, targetWorkspace, xml, isLabel);
|
|
|
|
/**
|
|
* Function to call when this button is clicked.
|
|
* @type {function(!Blockly.FlyoutButton)}
|
|
* @private
|
|
*/
|
|
this.callback_ = null;
|
|
|
|
var callbackKey = xml.getAttribute('callbackKey');
|
|
if (this.isLabel_ && callbackKey) {
|
|
console.warn('Labels should not have callbacks. Label text: ' + this.text_);
|
|
} else if (!this.isLabel_ &&
|
|
!(callbackKey && targetWorkspace.getButtonCallback(callbackKey))) {
|
|
console.warn('Buttons should have callbacks. Button text: ' + this.text_);
|
|
} else {
|
|
this.callback_ = targetWorkspace.getButtonCallback(callbackKey);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The margin around the text in the button.
|
|
*/
|
|
Blockly.FlyoutButton.MARGIN = 40;
|
|
|
|
/**
|
|
* The width of the button's rect.
|
|
* @type {number}
|
|
*/
|
|
Blockly.FlyoutButton.prototype.width = 0;
|
|
|
|
/**
|
|
* The height of the button's rect.
|
|
* @type {number}
|
|
*/
|
|
Blockly.FlyoutButton.prototype.height = 40; // Can't be computed like the width
|
|
|
|
/**
|
|
* Opaque data that can be passed to Blockly.unbindEvent_.
|
|
* @type {Array.<!Array>}
|
|
* @private
|
|
*/
|
|
Blockly.FlyoutButton.prototype.onMouseUpWrapper_ = null;
|
|
|
|
/**
|
|
* Initialize the button or label. This is a helper function to so that the
|
|
* constructor can be overridden.
|
|
* @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this
|
|
* button.
|
|
* @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace.
|
|
* @param {!Element} xml The XML specifying the label/button.
|
|
* @param {boolean} isLabel Whether this button should be styled as a label.
|
|
*/
|
|
Blockly.FlyoutButton.prototype.init = function(
|
|
workspace, targetWorkspace, xml, isLabel) {
|
|
|
|
/**
|
|
* @type {!Blockly.WorkspaceSvg}
|
|
* @private
|
|
*/
|
|
this.workspace_ = workspace;
|
|
|
|
/**
|
|
* @type {!Blockly.Workspace}
|
|
* @private
|
|
*/
|
|
this.targetWorkspace_ = targetWorkspace;
|
|
|
|
/**
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
this.text_ = xml.getAttribute('text');
|
|
|
|
/**
|
|
* @type {!goog.math.Coordinate}
|
|
* @private
|
|
*/
|
|
this.position_ = new goog.math.Coordinate(0, 0);
|
|
|
|
/**
|
|
* Whether this button should be styled as a label.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.isLabel_ = isLabel;
|
|
|
|
/**
|
|
* Whether this button is a label at the top of a category.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.isCategoryLabel_ = xml.getAttribute('category-label') === 'true';
|
|
|
|
/**
|
|
* If specified, a CSS class to add to this button.
|
|
* @type {?string}
|
|
* @private
|
|
*/
|
|
this.cssClass_ = xml.getAttribute('web-class') || null;
|
|
};
|
|
|
|
/**
|
|
* Create the button elements.
|
|
* @return {!Element} The button's SVG group.
|
|
*/
|
|
Blockly.FlyoutButton.prototype.createDom = function() {
|
|
var cssClass = this.isLabel_ ? 'blocklyFlyoutLabel' : 'blocklyFlyoutButton';
|
|
if (this.cssClass_) {
|
|
cssClass += ' ' + this.cssClass_;
|
|
}
|
|
|
|
this.svgGroup_ = Blockly.utils.createSvgElement('g', {'class': cssClass},
|
|
this.workspace_.getCanvas());
|
|
|
|
this.addTextSvg(this.isLabel_);
|
|
|
|
this.mouseUpWrapper_ = Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup',
|
|
this, this.onMouseUp_);
|
|
return this.svgGroup_;
|
|
};
|
|
|
|
/**
|
|
* Add the text element for the label or button.
|
|
* @param {boolean} isLabel True if this is a label and not button.
|
|
* @package
|
|
*/
|
|
Blockly.FlyoutButton.prototype.addTextSvg = function(isLabel) {
|
|
if (!isLabel) {
|
|
// Shadow rectangle (light source does not mirror in RTL).
|
|
var shadow = Blockly.utils.createSvgElement('rect',
|
|
{
|
|
'class': 'blocklyFlyoutButtonShadow',
|
|
'rx': 4,
|
|
'ry': 4,
|
|
'x': 1,
|
|
'y': 1
|
|
},
|
|
this.svgGroup_);
|
|
}
|
|
// Background rectangle.
|
|
var rect = Blockly.utils.createSvgElement('rect',
|
|
{
|
|
'class': isLabel ?
|
|
'blocklyFlyoutLabelBackground' : 'blocklyFlyoutButtonBackground',
|
|
'rx': 4, 'ry': 4
|
|
},
|
|
this.svgGroup_);
|
|
|
|
var svgText = Blockly.utils.createSvgElement('text',
|
|
{
|
|
'class': isLabel ? 'blocklyFlyoutLabelText' : 'blocklyText',
|
|
'x': 0,
|
|
'y': 0,
|
|
'text-anchor': 'middle'
|
|
},
|
|
this.svgGroup_);
|
|
svgText.textContent = Blockly.utils.replaceMessageReferences(this.text_);
|
|
|
|
this.width = Blockly.Field.getCachedWidth(svgText);
|
|
|
|
if (!isLabel) {
|
|
this.width += 2 * Blockly.FlyoutButton.MARGIN;
|
|
shadow.setAttribute('width', this.width);
|
|
shadow.setAttribute('height', this.height);
|
|
}
|
|
|
|
rect.setAttribute('width', this.width);
|
|
rect.setAttribute('height', this.height);
|
|
|
|
svgText.setAttribute('text-anchor', 'middle');
|
|
svgText.setAttribute('dominant-baseline', 'central');
|
|
svgText.setAttribute('dy', goog.userAgent.EDGE_OR_IE ?
|
|
Blockly.Field.IE_TEXT_OFFSET : '0');
|
|
svgText.setAttribute('x', this.width / 2);
|
|
svgText.setAttribute('y', this.height / 2);
|
|
};
|
|
|
|
/**
|
|
* Correctly position the flyout button and make it visible.
|
|
*/
|
|
Blockly.FlyoutButton.prototype.show = function() {
|
|
this.updateTransform_();
|
|
this.svgGroup_.setAttribute('display', 'block');
|
|
};
|
|
|
|
/**
|
|
* Update SVG attributes to match internal state.
|
|
* @private
|
|
*/
|
|
Blockly.FlyoutButton.prototype.updateTransform_ = function() {
|
|
this.svgGroup_.setAttribute('transform',
|
|
'translate(' + this.position_.x + ',' + this.position_.y + ')');
|
|
};
|
|
|
|
/**
|
|
* Move the button to the given x, y coordinates.
|
|
* @param {number} x The new x coordinate.
|
|
* @param {number} y The new y coordinate.
|
|
*/
|
|
Blockly.FlyoutButton.prototype.moveTo = function(x, y) {
|
|
this.position_.x = x;
|
|
this.position_.y = y;
|
|
this.updateTransform_();
|
|
};
|
|
|
|
/**
|
|
* Get the button's target workspace.
|
|
* @return {!Blockly.WorkspaceSvg} The target workspace of the flyout where this
|
|
* button resides.
|
|
*/
|
|
Blockly.FlyoutButton.prototype.getTargetWorkspace = function() {
|
|
return this.targetWorkspace_;
|
|
};
|
|
|
|
/**
|
|
* Get whether this button is a label at the top of a category.
|
|
* @return {boolean} True if it is a category label.
|
|
* @package
|
|
*/
|
|
Blockly.FlyoutButton.prototype.getIsCategoryLabel = function() {
|
|
return this.isCategoryLabel_;
|
|
};
|
|
|
|
/**
|
|
* Get the text of this button.
|
|
* @return {string} The text on the button.
|
|
* @package
|
|
*/
|
|
Blockly.FlyoutButton.prototype.getText = function() {
|
|
return this.text_;
|
|
};
|
|
|
|
/**
|
|
* Get the position of this button.
|
|
* @return {!goog.math.Coordinate} The button position.
|
|
* @package
|
|
*/
|
|
Blockly.FlyoutButton.prototype.getPosition = function() {
|
|
return this.position_;
|
|
};
|
|
|
|
/**
|
|
* Dispose of this button.
|
|
*/
|
|
Blockly.FlyoutButton.prototype.dispose = function() {
|
|
if (this.onMouseUpWrapper_) {
|
|
Blockly.unbindEvent_(this.onMouseUpWrapper_);
|
|
}
|
|
if (this.svgGroup_) {
|
|
goog.dom.removeNode(this.svgGroup_);
|
|
this.svgGroup_ = null;
|
|
}
|
|
this.workspace_ = null;
|
|
this.targetWorkspace_ = null;
|
|
};
|
|
|
|
/**
|
|
* Do something when the button is clicked.
|
|
* @param {!Event} e Mouse up event.
|
|
* @private
|
|
*/
|
|
Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) {
|
|
var gesture = this.targetWorkspace_.getGesture(e);
|
|
if (gesture) {
|
|
// If we're in the middle of dragging something (blocks, workspace, etc.) ignore the button.
|
|
// Otherwise, cancel the gesture.
|
|
if (gesture.isDragging()) {
|
|
return;
|
|
}
|
|
gesture.cancel();
|
|
}
|
|
|
|
// Call the callback registered to this button.
|
|
if (this.callback_) {
|
|
this.callback_(this);
|
|
}
|
|
};
|