mirror of
https://github.com/google/blockly.git
synced 2026-01-30 20:20:09 +01:00
Merge pull request #736 from google/fraser-develop
Allow images in dropdown menus.
This commit is contained in:
@@ -631,7 +631,7 @@ Blockly.Block.prototype.getColour = function() {
|
||||
* @param {number|string} colour HSV hue value, or #RRGGBB string.
|
||||
*/
|
||||
Blockly.Block.prototype.setColour = function(colour) {
|
||||
var hue = parseFloat(colour);
|
||||
var hue = Number(colour);
|
||||
if (!isNaN(hue)) {
|
||||
this.colour_ = Blockly.hueToRgb(hue);
|
||||
} else if (goog.isString(colour) && colour.match(/^#[0-9a-fA-F]{6}$/)) {
|
||||
|
||||
@@ -82,7 +82,8 @@ Blockly.ContextMenu.show = function(e, options, rtl) {
|
||||
var menuDom = menu.getElement();
|
||||
Blockly.addClass_(menuDom, 'blocklyContextMenu');
|
||||
// Prevent system context menu when right-clicking a Blockly context menu.
|
||||
Blockly.bindEventWithChecks_(menuDom, 'contextmenu', null, Blockly.noEvent);
|
||||
Blockly.bindEventWithChecks_(menuDom, 'contextmenu', null,
|
||||
Blockly.utils.noEvent);
|
||||
// Record menuSize after adding menu.
|
||||
var menuSize = goog.style.getSize(menuDom);
|
||||
|
||||
|
||||
127
core/field.js
127
core/field.js
@@ -45,7 +45,7 @@ goog.require('goog.userAgent');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Field = function(text, opt_validator) {
|
||||
this.size_ = new goog.math.Size(0, 25);
|
||||
this.size_ = new goog.math.Size(0, Blockly.BlockSvg.MIN_BLOCK_Y);
|
||||
this.setValue(text);
|
||||
this.setValidator(opt_validator);
|
||||
};
|
||||
@@ -156,7 +156,7 @@ Blockly.Field.prototype.init = function() {
|
||||
Blockly.bindEventWithChecks_(this.fieldGroup_, 'mouseup', this,
|
||||
this.onMouseUp_);
|
||||
// Force a render.
|
||||
this.updateTextNode_();
|
||||
this.render_();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -285,33 +285,48 @@ Blockly.Field.prototype.getSvgRoot = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Field.prototype.render_ = function() {
|
||||
if (this.visible_ && this.textElement_) {
|
||||
var key = this.textElement_.textContent + '\n' +
|
||||
this.textElement_.className.baseVal;
|
||||
if (Blockly.Field.cacheWidths_ && Blockly.Field.cacheWidths_[key]) {
|
||||
var width = Blockly.Field.cacheWidths_[key];
|
||||
} else {
|
||||
try {
|
||||
var width = this.textElement_.getComputedTextLength();
|
||||
} catch (e) {
|
||||
// MSIE 11 is known to throw "Unexpected call to method or property
|
||||
// access." if Blockly is hidden.
|
||||
var width = this.textElement_.textContent.length * 8;
|
||||
}
|
||||
if (Blockly.Field.cacheWidths_) {
|
||||
Blockly.Field.cacheWidths_[key] = width;
|
||||
}
|
||||
}
|
||||
if (this.borderRect_) {
|
||||
this.borderRect_.setAttribute('width',
|
||||
width + Blockly.BlockSvg.SEP_SPACE_X);
|
||||
}
|
||||
} else {
|
||||
var width = 0;
|
||||
if (!this.visible_) {
|
||||
this.size_.width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace the text.
|
||||
goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
|
||||
var textNode = document.createTextNode(this.getDisplayText_());
|
||||
this.textElement_.appendChild(textNode);
|
||||
|
||||
var width = Blockly.Field.getCachedWidth(this.textElement_);
|
||||
if (this.borderRect_) {
|
||||
this.borderRect_.setAttribute('width',
|
||||
width + Blockly.BlockSvg.SEP_SPACE_X);
|
||||
}
|
||||
this.size_.width = width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the width of a text element, caching it in the process.
|
||||
* @param {!Element} textElement An SVG 'text' element.
|
||||
* @retur {number} Width of element.
|
||||
*/
|
||||
Blockly.Field.getCachedWidth = function(textElement) {
|
||||
var key = textElement.textContent + '\n' + textElement.className.baseVal;
|
||||
if (Blockly.Field.cacheWidths_ && Blockly.Field.cacheWidths_[key]) {
|
||||
var width = Blockly.Field.cacheWidths_[key];
|
||||
} else {
|
||||
try {
|
||||
var width = textElement.getComputedTextLength();
|
||||
} catch (e) {
|
||||
// MSIE 11 is known to throw "Unexpected call to method or property
|
||||
// access." if Blockly is hidden.
|
||||
var width = textElement.textContent.length * 8;
|
||||
}
|
||||
if (Blockly.Field.cacheWidths_) {
|
||||
Blockly.Field.cacheWidths_[key] = width;
|
||||
}
|
||||
}
|
||||
return width;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start caching field widths. Every call to this function MUST also call
|
||||
* stopCache. Caches must not survive between execution threads.
|
||||
@@ -358,6 +373,31 @@ Blockly.Field.prototype.getScaledBBox_ = function() {
|
||||
bBox.height * this.sourceBlock_.workspace.scale);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text from this field as displayed on screen. May differ from getText
|
||||
* due to ellipsis, and other formatting.
|
||||
* @return {string} Currently displayed text.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Field.prototype.getDisplayText_ = function() {
|
||||
var text = this.text_;
|
||||
if (!text) {
|
||||
// Prevent the field from disappearing if empty.
|
||||
return Blockly.Field.NBSP;
|
||||
}
|
||||
if (text.length > this.maxDisplayLength) {
|
||||
// Truncate displayed string and add an ellipsis ('...').
|
||||
text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
|
||||
}
|
||||
// Replace whitespace with non-breaking spaces so the text doesn't collapse.
|
||||
text = text.replace(/\s/g, Blockly.Field.NBSP);
|
||||
if (this.sourceBlock_.RTL) {
|
||||
// The SVG is LTR, force text to be RTL.
|
||||
text += '\u200F';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text from this field.
|
||||
* @return {string} Current text.
|
||||
@@ -381,7 +421,8 @@ Blockly.Field.prototype.setText = function(text) {
|
||||
return;
|
||||
}
|
||||
this.text_ = text;
|
||||
this.updateTextNode_();
|
||||
// Set width to 0 to force a rerender of this field.
|
||||
this.size_.width = 0;
|
||||
|
||||
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
|
||||
this.sourceBlock_.render();
|
||||
@@ -389,40 +430,6 @@ Blockly.Field.prototype.setText = function(text) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the text node of this field to display the current text.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Field.prototype.updateTextNode_ = function() {
|
||||
if (!this.textElement_) {
|
||||
// Not rendered yet.
|
||||
return;
|
||||
}
|
||||
var text = this.text_;
|
||||
if (text.length > this.maxDisplayLength) {
|
||||
// Truncate displayed string and add an ellipsis ('...').
|
||||
text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
|
||||
}
|
||||
// Replace whitespace with non-breaking spaces so the text doesn't collapse.
|
||||
text = text.replace(/\s/g, Blockly.Field.NBSP);
|
||||
if (this.sourceBlock_.RTL && text) {
|
||||
// The SVG is LTR, force text to be RTL.
|
||||
text += '\u200F';
|
||||
}
|
||||
if (!text) {
|
||||
// Prevent the field from disappearing if empty.
|
||||
text = Blockly.Field.NBSP;
|
||||
}
|
||||
|
||||
// Replace the text.
|
||||
goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
|
||||
var textNode = document.createTextNode(text);
|
||||
this.textElement_.appendChild(textNode);
|
||||
|
||||
// Cached width is obsolete. Clear it.
|
||||
this.size_.width = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* By default there is no difference between the human-readable text and
|
||||
* the language-neutral values. Subclasses (such as dropdown) may define this.
|
||||
|
||||
@@ -39,8 +39,8 @@ goog.require('goog.userAgent');
|
||||
|
||||
/**
|
||||
* Class for an editable dropdown field.
|
||||
* @param {(!Array.<!Array.<string>>|!Function)} menuGenerator An array of
|
||||
* options for a dropdown list, or a function which generates these options.
|
||||
* @param {(!Array.<!Array>|!Function)} menuGenerator An array of options
|
||||
* for a dropdown list, or a function which generates these options.
|
||||
* @param {Function=} opt_validator A function that is executed when a new
|
||||
* option is selected, with the newly selected value as its sole argument.
|
||||
* If it returns a value, that value (which must be one of the options) will
|
||||
@@ -76,12 +76,27 @@ Blockly.FieldDropdown.ARROW_CHAR = goog.userAgent.ANDROID ? '\u25BC' : '\u25BE';
|
||||
Blockly.FieldDropdown.prototype.CURSOR = 'default';
|
||||
|
||||
/**
|
||||
* Language-neutral currently selected string.
|
||||
* @type {string}
|
||||
* Language-neutral currently selected string or image object.
|
||||
* @type {string|!Object}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.value_ = '';
|
||||
|
||||
/**
|
||||
* SVG image element if currently selected option is an image, or null.
|
||||
* @type {SVGElement}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.imageElement_ = null;
|
||||
|
||||
/**
|
||||
* Object wih src, height, width, and alt attributes if currently selected
|
||||
* option is an image, or null.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.imageJson_ = null;
|
||||
|
||||
/**
|
||||
* Install this dropdown on a block.
|
||||
*/
|
||||
@@ -124,9 +139,16 @@ Blockly.FieldDropdown.prototype.showEditor_ = function() {
|
||||
menu.setRightToLeft(this.sourceBlock_.RTL);
|
||||
var options = this.getOptions_();
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
var text = options[i][0]; // Human-readable text.
|
||||
var value = options[i][1]; // Language-neutral value.
|
||||
var menuItem = new goog.ui.MenuItem(text);
|
||||
var content = options[i][0]; // Human-readable text or image.
|
||||
var value = options[i][1]; // Language-neutral value.
|
||||
if (typeof content == 'object') {
|
||||
// An image, not text.
|
||||
var image = new Image(content['width'], content['height']);
|
||||
image.src = content['src'];
|
||||
image.alt = content['alt'] || '';
|
||||
content = image;
|
||||
}
|
||||
var menuItem = new goog.ui.MenuItem(content);
|
||||
menuItem.setRightToLeft(this.sourceBlock_.RTL);
|
||||
menuItem.setValue(value);
|
||||
menuItem.setCheckable(true);
|
||||
@@ -221,7 +243,14 @@ Blockly.FieldDropdown.prototype.trimOptions_ = function() {
|
||||
if (!goog.isArray(options) || options.length < 2) {
|
||||
return;
|
||||
}
|
||||
var strings = options.map(function(t) {return t[0];});
|
||||
var strings = [];
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
var text = options[i][0];
|
||||
if (typeof text != 'string') {
|
||||
return; // No text splitting if there is an image in the list.
|
||||
}
|
||||
strings.push(text);
|
||||
}
|
||||
var shortest = Blockly.shortestStringLength(strings);
|
||||
var prefixLength = Blockly.commonWordPrefix(strings, shortest);
|
||||
var suffixLength = Blockly.commonWordSuffix(strings, shortest);
|
||||
@@ -251,8 +280,8 @@ Blockly.FieldDropdown.prototype.trimOptions_ = function() {
|
||||
|
||||
/**
|
||||
* Return a list of the options for this dropdown.
|
||||
* @return {!Array.<!Array.<string>>} Array of option tuples:
|
||||
* (human-readable text, language-neutral name).
|
||||
* @return {!Array.<!Array>} Array of option tuples:
|
||||
* (human-readable text or image, language-neutral name).
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.getOptions_ = function() {
|
||||
@@ -288,7 +317,14 @@ Blockly.FieldDropdown.prototype.setValue = function(newValue) {
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
// Options are tuples of human-readable text and language-neutral values.
|
||||
if (options[i][1] == newValue) {
|
||||
this.setText(options[i][0]);
|
||||
var content = options[i][0];
|
||||
if (typeof content == 'object') {
|
||||
this.imageJson_ = content;
|
||||
this.setText(content.alt);
|
||||
} else {
|
||||
this.imageJson_ = null;
|
||||
this.setText(content);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -298,34 +334,63 @@ Blockly.FieldDropdown.prototype.setValue = function(newValue) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the text in this field. Trigger a rerender of the source block.
|
||||
* @param {?string} text New text.
|
||||
* Draws the border with the correct width.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.setText = function(text) {
|
||||
Blockly.FieldDropdown.prototype.render_ = function() {
|
||||
if (!this.visible_) {
|
||||
this.size_.width = 0;
|
||||
return;
|
||||
}
|
||||
if (this.sourceBlock_ && this.arrow_) {
|
||||
// Update arrow's colour.
|
||||
this.arrow_.style.fill = this.sourceBlock_.getColour();
|
||||
}
|
||||
if (text === null || text === this.text_) {
|
||||
// No change if null.
|
||||
return;
|
||||
}
|
||||
this.text_ = text;
|
||||
this.updateTextNode_();
|
||||
goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
|
||||
goog.dom.removeNode(this.imageElement_);
|
||||
this.imageElement_ = null;
|
||||
|
||||
if (this.textElement_) {
|
||||
if (this.imageJson_) {
|
||||
// Image option is selected.
|
||||
this.imageElement_ = Blockly.createSvgElement('image',
|
||||
{'y': 5,
|
||||
'height': this.imageJson_.height + 'px',
|
||||
'width': this.imageJson_.width + 'px'}, this.fieldGroup_);
|
||||
this.imageElement_.setAttributeNS('http://www.w3.org/1999/xlink',
|
||||
'xlink:href', this.imageJson_.src);
|
||||
// Insert dropdown arrow.
|
||||
this.textElement_.appendChild(this.arrow_);
|
||||
var arrowWidth = Blockly.Field.getCachedWidth(this.arrow_);
|
||||
this.size_.height = Number(this.imageJson_.height) + 19;
|
||||
this.size_.width = Number(this.imageJson_.width) + arrowWidth;
|
||||
if (this.sourceBlock_.RTL) {
|
||||
this.imageElement_.setAttribute('x', arrowWidth);
|
||||
this.textElement_.setAttribute('x', -1);
|
||||
} else {
|
||||
this.textElement_.setAttribute('text-anchor', 'end');
|
||||
this.textElement_.setAttribute('x', this.size_.width + 1);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Text option is selected.
|
||||
// Replace the text.
|
||||
var textNode = document.createTextNode(this.getDisplayText_());
|
||||
this.textElement_.appendChild(textNode);
|
||||
// Insert dropdown arrow.
|
||||
if (this.sourceBlock_.RTL) {
|
||||
this.textElement_.insertBefore(this.arrow_, this.textElement_.firstChild);
|
||||
} else {
|
||||
this.textElement_.appendChild(this.arrow_);
|
||||
}
|
||||
}
|
||||
this.textElement_.setAttribute('text-anchor', 'start');
|
||||
this.textElement_.setAttribute('x', 0);
|
||||
|
||||
if (this.sourceBlock_ && this.sourceBlock_.rendered) {
|
||||
this.sourceBlock_.render();
|
||||
this.sourceBlock_.bumpNeighbours_();
|
||||
this.size_.height = Blockly.BlockSvg.MIN_BLOCK_Y;
|
||||
this.size_.width = Blockly.Field.getCachedWidth(this.textElement_);
|
||||
}
|
||||
this.borderRect_.setAttribute('height', this.size_.height - 9);
|
||||
this.borderRect_.setAttribute('width',
|
||||
this.size_.width + Blockly.BlockSvg.SEP_SPACE_X);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,7 +74,7 @@ Blockly.FieldLabel.prototype.init = function() {
|
||||
this.textElement_.tooltip = this.sourceBlock_;
|
||||
Blockly.Tooltip.bindMouseEvents(this.textElement_);
|
||||
// Force a render.
|
||||
this.updateTextNode_();
|
||||
this.render_();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,7 +92,7 @@ Blockly.hasClass_ = function(element, className) {
|
||||
|
||||
/**
|
||||
* Bind an event to a function call. When calling the function, verifies that
|
||||
* it belongs to the touch stream that is currently being processsed, and splits
|
||||
* it belongs to the touch stream that is currently being processed, and splits
|
||||
* multitouch events into multiple events as needed.
|
||||
* @param {!Node} node Node upon which to listen.
|
||||
* @param {string} name Event name to listen to (e.g. 'mousedown').
|
||||
@@ -219,7 +219,7 @@ Blockly.unbindEvent_ = function(bindData) {
|
||||
* Don't do anything for this event, just halt propagation.
|
||||
* @param {!Event} e An event.
|
||||
*/
|
||||
Blockly.noEvent = function(e) {
|
||||
Blockly.utils.noEvent = function(e) {
|
||||
// This event has been handled. No need to bubble up to the document.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -561,7 +561,7 @@ Blockly.genUid.soup_ = '!#$%()*+,-./:;=?@[]^_`{|}~' +
|
||||
Blockly.utils.wrap = function(text, limit) {
|
||||
var lines = text.split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
lines[i] = Blockly.utils.wrap_line_(lines[i], limit);
|
||||
lines[i] = Blockly.utils.wrapLine_(lines[i], limit);
|
||||
}
|
||||
return lines.join('\n');
|
||||
};
|
||||
@@ -573,7 +573,7 @@ Blockly.utils.wrap = function(text, limit) {
|
||||
* @return {string} Wrapped text.
|
||||
* @private
|
||||
*/
|
||||
Blockly.utils.wrap_line_ = function(text, limit) {
|
||||
Blockly.utils.wrapLine_ = function(text, limit) {
|
||||
if (text.length <= limit) {
|
||||
// Short text, no need to wrap.
|
||||
return text;
|
||||
|
||||
@@ -616,18 +616,20 @@ AppController.prototype.onresize = function(event) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for the window's 'onbeforeunload' event. When a user has unsaved
|
||||
* Handler for the window's 'beforeunload' event. When a user has unsaved
|
||||
* changes and refreshes or leaves the page, confirm that they want to do so
|
||||
* before actually refreshing.
|
||||
* @param {!Event} e beforeunload event.
|
||||
*/
|
||||
AppController.prototype.confirmLeavePage = function() {
|
||||
AppController.prototype.confirmLeavePage = function(e) {
|
||||
if ((!BlockFactory.isStarterBlock() &&
|
||||
!FactoryUtils.savedBlockChanges(this.blockLibraryController)) ||
|
||||
this.workspaceFactoryController.hasUnsavedChanges()) {
|
||||
// When a string is assigned to the returnValue Event property, a dialog box
|
||||
// appears, asking the users for confirmation to leave the page.
|
||||
return 'You will lose any unsaved changes. Are you sure you want ' +
|
||||
'to exit this page?';
|
||||
!FactoryUtils.savedBlockChanges(blocklyFactory.blockLibraryController)) ||
|
||||
blocklyFactory.workspaceFactoryController.hasUnsavedChanges()) {
|
||||
|
||||
var confirmationMessage = 'You will lose any unsaved changes. ' +
|
||||
'Are you sure you want to exit this page?';
|
||||
e.returnValue = confirmationMessage;
|
||||
return confirmationMessage;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Blockly.Blocks['factory_base'] = {
|
||||
['↓ bottom connection', 'BOTTOM']],
|
||||
function(option) {
|
||||
this.sourceBlock_.updateShape_(option);
|
||||
// Connect a shadow block to this new input.
|
||||
// Connect a shadow block to this new input.
|
||||
this.sourceBlock_.spawnOutputShadow_(option);
|
||||
});
|
||||
this.appendDummyInput()
|
||||
@@ -318,11 +318,12 @@ Blockly.Blocks['field_dropdown'] = {
|
||||
this.appendDummyInput()
|
||||
.appendField('dropdown')
|
||||
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
|
||||
this.optionCount_ = 3;
|
||||
this.optionList_ = ['text', 'text', 'text'];
|
||||
this.updateShape_();
|
||||
this.setPreviousStatement(true, 'Field');
|
||||
this.setNextStatement(true, 'Field');
|
||||
this.setMutator(new Blockly.Mutator(['field_dropdown_option']));
|
||||
this.setMutator(new Blockly.Mutator(['field_dropdown_option_text',
|
||||
'field_dropdown_option_image']));
|
||||
this.setColour(160);
|
||||
this.setTooltip('Dropdown menu with a list of options.');
|
||||
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
|
||||
@@ -330,12 +331,21 @@ Blockly.Blocks['field_dropdown'] = {
|
||||
mutationToDom: function(workspace) {
|
||||
// Create XML to represent menu options.
|
||||
var container = document.createElement('mutation');
|
||||
container.setAttribute('options', this.optionCount_);
|
||||
container.setAttribute('options', JSON.stringify(this.optionList_));
|
||||
return container;
|
||||
},
|
||||
domToMutation: function(container) {
|
||||
// Parse XML to restore the menu options.
|
||||
this.optionCount_ = parseInt(container.getAttribute('options'), 10);
|
||||
var value = JSON.parse(container.getAttribute('options'));
|
||||
if (typeof value == 'number') {
|
||||
// Old format from before images were added. November 2016.
|
||||
this.optionList_ = [];
|
||||
for (var i = 0; i < value; i++) {
|
||||
this.optionList_.push('text');
|
||||
}
|
||||
} else {
|
||||
this.optionList_ = value;
|
||||
}
|
||||
this.updateShape_();
|
||||
},
|
||||
decompose: function(workspace) {
|
||||
@@ -343,8 +353,9 @@ Blockly.Blocks['field_dropdown'] = {
|
||||
var containerBlock = workspace.newBlock('field_dropdown_container');
|
||||
containerBlock.initSvg();
|
||||
var connection = containerBlock.getInput('STACK').connection;
|
||||
for (var i = 0; i < this.optionCount_; i++) {
|
||||
var optionBlock = workspace.newBlock('field_dropdown_option');
|
||||
for (var i = 0; i < this.optionList_.length; i++) {
|
||||
var optionBlock = workspace.newBlock(
|
||||
'field_dropdown_option_' + this.optionList_[i]);
|
||||
optionBlock.initSvg();
|
||||
connection.connect(optionBlock.previousConnection);
|
||||
connection = optionBlock.nextConnection;
|
||||
@@ -355,26 +366,41 @@ Blockly.Blocks['field_dropdown'] = {
|
||||
// Reconfigure this block based on the mutator dialog's components.
|
||||
var optionBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
// Count number of inputs.
|
||||
this.optionList_.length = 0;
|
||||
var data = [];
|
||||
while (optionBlock) {
|
||||
if (optionBlock.type == 'field_dropdown_option_text') {
|
||||
this.optionList_.push('text');
|
||||
} else if (optionBlock.type == 'field_dropdown_option_image') {
|
||||
this.optionList_.push('image');
|
||||
}
|
||||
data.push([optionBlock.userData_, optionBlock.cpuData_]);
|
||||
optionBlock = optionBlock.nextConnection &&
|
||||
optionBlock.nextConnection.targetBlock();
|
||||
}
|
||||
this.optionCount_ = data.length;
|
||||
this.updateShape_();
|
||||
// Restore any data.
|
||||
for (var i = 0; i < this.optionCount_; i++) {
|
||||
this.setFieldValue(data[i][0] || 'option', 'USER' + i);
|
||||
this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
|
||||
for (var i = 0; i < this.optionList_.length; i++) {
|
||||
var userData = data[i][0];
|
||||
if (userData !== undefined) {
|
||||
if (typeof userData == 'string') {
|
||||
this.setFieldValue(userData || 'option', 'USER' + i);
|
||||
} else {
|
||||
this.setFieldValue(userData.src, 'SRC' + i);
|
||||
this.setFieldValue(userData.width, 'WIDTH' + i);
|
||||
this.setFieldValue(userData.height, 'HEIGHT' + i);
|
||||
this.setFieldValue(userData.alt, 'ALT' + i);
|
||||
}
|
||||
this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
|
||||
}
|
||||
}
|
||||
},
|
||||
saveConnections: function(containerBlock) {
|
||||
// Store names and values for each option.
|
||||
// Store all data for each option.
|
||||
var optionBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
var i = 0;
|
||||
while (optionBlock) {
|
||||
optionBlock.userData_ = this.getFieldValue('USER' + i);
|
||||
optionBlock.userData_ = this.getUserData(i);
|
||||
optionBlock.cpuData_ = this.getFieldValue('CPU' + i);
|
||||
i++;
|
||||
optionBlock = optionBlock.nextConnection &&
|
||||
@@ -382,28 +408,61 @@ Blockly.Blocks['field_dropdown'] = {
|
||||
}
|
||||
},
|
||||
updateShape_: function() {
|
||||
// Modify this block to have the correct number of options.
|
||||
// Add new options.
|
||||
for (var i = 0; i < this.optionCount_; i++) {
|
||||
if (!this.getInput('OPTION' + i)) {
|
||||
// Delete everything.
|
||||
var i = 0;
|
||||
while (this.getInput('OPTION' + i)) {
|
||||
this.removeInput('OPTION' + i);
|
||||
this.removeInput('OPTION_IMAGE' + i, true);
|
||||
i++;
|
||||
}
|
||||
// Rebuild block.
|
||||
var src = 'https://www.gstatic.com/codesite/ph/images/star_on.gif';
|
||||
for (var i = 0; i <= this.optionList_.length; i++) {
|
||||
var type = this.optionList_[i];
|
||||
if (type == 'text') {
|
||||
this.appendDummyInput('OPTION' + i)
|
||||
.appendField('•')
|
||||
.appendField(new Blockly.FieldTextInput('option'), 'USER' + i)
|
||||
.appendField(',')
|
||||
.appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
|
||||
} else if (type == 'image') {
|
||||
this.appendDummyInput('OPTION' + i)
|
||||
.appendField('•')
|
||||
.appendField('image')
|
||||
.appendField(new Blockly.FieldTextInput(src), 'SRC' + i);
|
||||
this.appendDummyInput('OPTION_IMAGE' + i)
|
||||
.appendField(' ')
|
||||
.appendField('width')
|
||||
.appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH' + i)
|
||||
.appendField('height')
|
||||
.appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT' + i)
|
||||
.appendField('alt text')
|
||||
.appendField(new Blockly.FieldTextInput('*'), 'ALT' + i)
|
||||
.appendField(',')
|
||||
.appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
|
||||
}
|
||||
}
|
||||
// Remove deleted options.
|
||||
while (this.getInput('OPTION' + i)) {
|
||||
this.removeInput('OPTION' + i);
|
||||
i++;
|
||||
}
|
||||
},
|
||||
onchange: function() {
|
||||
if (this.workspace && this.optionCount_ < 1) {
|
||||
if (this.workspace && this.optionList_.length < 1) {
|
||||
this.setWarningText('Drop down menu must\nhave at least one option.');
|
||||
} else {
|
||||
fieldNameCheck(this);
|
||||
}
|
||||
},
|
||||
getUserData: function(n) {
|
||||
if (this.optionList_[n] == 'text') {
|
||||
return this.getFieldValue('USER' + n);
|
||||
}
|
||||
if (this.optionList_[n] == 'image') {
|
||||
return {
|
||||
src: this.getFieldValue('SRC' + n),
|
||||
width: Number(this.getFieldValue('WIDTH' + n)),
|
||||
height: Number(this.getFieldValue('HEIGHT' + n)),
|
||||
alt: this.getFieldValue('ALT' + n)
|
||||
};
|
||||
}
|
||||
throw 'Unknown dropdown type';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -421,15 +480,29 @@ Blockly.Blocks['field_dropdown_container'] = {
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['field_dropdown_option'] = {
|
||||
// Add option.
|
||||
Blockly.Blocks['field_dropdown_option_text'] = {
|
||||
// Add text option.
|
||||
init: function() {
|
||||
this.setColour(160);
|
||||
this.appendDummyInput()
|
||||
.appendField('option');
|
||||
.appendField('text option');
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.setTooltip('Add a new option to the dropdown menu.');
|
||||
this.setTooltip('Add a new text option to the dropdown menu.');
|
||||
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
|
||||
this.contextMenu = false;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['field_dropdown_option_image'] = {
|
||||
// Add image option.
|
||||
init: function() {
|
||||
this.setColour(160);
|
||||
this.appendDummyInput()
|
||||
.appendField('image option');
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.setTooltip('Add a new image option to the dropdown menu.');
|
||||
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
|
||||
this.contextMenu = false;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
@@ -278,7 +278,7 @@ FactoryUtils.formatJavaScript_ = function(blockType, rootBlock, workspace) {
|
||||
// Dummy inputs don't have names. Other inputs do.
|
||||
if (contentsBlock.type != 'input_dummy') {
|
||||
name =
|
||||
FactoryUtils.escapeString(contentsBlock.getFieldValue('INPUTNAME'));
|
||||
JSON.stringify(contentsBlock.getFieldValue('INPUTNAME'));
|
||||
}
|
||||
code.push(' this.' + TYPES[contentsBlock.type] + '(' + name + ')');
|
||||
var check = FactoryUtils.getOptTypesFrom(contentsBlock, 'TYPE');
|
||||
@@ -373,13 +373,13 @@ FactoryUtils.getFieldsJs_ = function(block) {
|
||||
switch (block.type) {
|
||||
case 'field_static':
|
||||
// Result: 'hello'
|
||||
fields.push(FactoryUtils.escapeString(block.getFieldValue('TEXT')));
|
||||
fields.push(JSON.stringify(block.getFieldValue('TEXT')));
|
||||
break;
|
||||
case 'field_input':
|
||||
// Result: new Blockly.FieldTextInput('Hello'), 'GREET'
|
||||
fields.push('new Blockly.FieldTextInput(' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('TEXT')) + '), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('TEXT')) + '), ' +
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
break;
|
||||
case 'field_number':
|
||||
// Result: new Blockly.FieldNumber(10, 0, 100, 1), 'NUMBER'
|
||||
@@ -400,63 +400,61 @@ FactoryUtils.getFieldsJs_ = function(block) {
|
||||
}
|
||||
}
|
||||
fields.push('new Blockly.FieldNumber(' + args.join(', ') + '), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
break;
|
||||
case 'field_angle':
|
||||
// Result: new Blockly.FieldAngle(90), 'ANGLE'
|
||||
fields.push('new Blockly.FieldAngle(' +
|
||||
parseFloat(block.getFieldValue('ANGLE')) + '), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
break;
|
||||
case 'field_checkbox':
|
||||
// Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
|
||||
fields.push('new Blockly.FieldCheckbox(' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('CHECKED')) +
|
||||
JSON.stringify(block.getFieldValue('CHECKED')) +
|
||||
'), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
break;
|
||||
case 'field_colour':
|
||||
// Result: new Blockly.FieldColour('#ff0000'), 'COLOUR'
|
||||
fields.push('new Blockly.FieldColour(' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('COLOUR')) +
|
||||
JSON.stringify(block.getFieldValue('COLOUR')) +
|
||||
'), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
break;
|
||||
case 'field_date':
|
||||
// Result: new Blockly.FieldDate('2015-02-04'), 'DATE'
|
||||
fields.push('new Blockly.FieldDate(' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('DATE')) + '), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('DATE')) + '), ' +
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
break;
|
||||
case 'field_variable':
|
||||
// Result: new Blockly.FieldVariable('item'), 'VAR'
|
||||
var varname
|
||||
= FactoryUtils.escapeString(block.getFieldValue('TEXT') || null);
|
||||
= JSON.stringify(block.getFieldValue('TEXT') || null);
|
||||
fields.push('new Blockly.FieldVariable(' + varname + '), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
break;
|
||||
case 'field_dropdown':
|
||||
// Result:
|
||||
// new Blockly.FieldDropdown([['yes', '1'], ['no', '0']]), 'TOGGLE'
|
||||
var options = [];
|
||||
for (var i = 0; i < block.optionCount_; i++) {
|
||||
options[i] = '[' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('USER' + i)) +
|
||||
', ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('CPU' + i)) + ']';
|
||||
for (var i = 0; i < block.optionList_.length; i++) {
|
||||
options[i] = JSON.stringify([block.getUserData(i),
|
||||
block.getFieldValue('CPU' + i)]);
|
||||
}
|
||||
if (options.length) {
|
||||
fields.push('new Blockly.FieldDropdown([' +
|
||||
options.join(', ') + ']), ' +
|
||||
FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
|
||||
JSON.stringify(block.getFieldValue('FIELDNAME')));
|
||||
}
|
||||
break;
|
||||
case 'field_image':
|
||||
// Result: new Blockly.FieldImage('http://...', 80, 60)
|
||||
var src = FactoryUtils.escapeString(block.getFieldValue('SRC'));
|
||||
// Result: new Blockly.FieldImage('http://...', 80, 60, '*')
|
||||
var src = JSON.stringify(block.getFieldValue('SRC'));
|
||||
var width = Number(block.getFieldValue('WIDTH'));
|
||||
var height = Number(block.getFieldValue('HEIGHT'));
|
||||
var alt = FactoryUtils.escapeString(block.getFieldValue('ALT'));
|
||||
var alt = JSON.stringify(block.getFieldValue('ALT'));
|
||||
fields.push('new Blockly.FieldImage(' +
|
||||
src + ', ' + width + ', ' + height + ', ' + alt + ')');
|
||||
break;
|
||||
@@ -546,8 +544,8 @@ FactoryUtils.getFieldsJson_ = function(block) {
|
||||
break;
|
||||
case 'field_dropdown':
|
||||
var options = [];
|
||||
for (var i = 0; i < block.optionCount_; i++) {
|
||||
options[i] = [block.getFieldValue('USER' + i),
|
||||
for (var i = 0; i < block.optionList_.length; i++) {
|
||||
options[i] = [block.getUserData(i),
|
||||
block.getFieldValue('CPU' + i)];
|
||||
}
|
||||
if (options.length) {
|
||||
@@ -608,7 +606,7 @@ FactoryUtils.getTypesFrom_ = function(block, name) {
|
||||
if (!typeBlock || typeBlock.disabled) {
|
||||
types = [];
|
||||
} else if (typeBlock.type == 'type_other') {
|
||||
types = [FactoryUtils.escapeString(typeBlock.getFieldValue('TYPE'))];
|
||||
types = [JSON.stringify(typeBlock.getFieldValue('TYPE'))];
|
||||
} else if (typeBlock.type == 'type_group') {
|
||||
types = [];
|
||||
for (var n = 0; n < typeBlock.typeCount_; n++) {
|
||||
@@ -623,20 +621,11 @@ FactoryUtils.getTypesFrom_ = function(block, name) {
|
||||
hash[types[n]] = true;
|
||||
}
|
||||
} else {
|
||||
types = [FactoryUtils.escapeString(typeBlock.valueType)];
|
||||
types = [JSON.stringify(typeBlock.valueType)];
|
||||
}
|
||||
return types;
|
||||
};
|
||||
|
||||
/**
|
||||
* Escape a string.
|
||||
* @param {string} string String to escape.
|
||||
* @return {string} Escaped string surrouned by quotes.
|
||||
*/
|
||||
FactoryUtils.escapeString = function(string) {
|
||||
return JSON.stringify(string);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the uneditable container block that everything else attaches to in
|
||||
* given workspace.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,5 +1,3 @@
|
||||
<!-- TODO(quachtina96): move the CSS out to a separate file -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -37,11 +35,12 @@
|
||||
var init = function() {
|
||||
blocklyFactory = new AppController();
|
||||
blocklyFactory.init();
|
||||
window.addEventListener('beforeunload', blocklyFactory.confirmLeavePage);
|
||||
};
|
||||
window.addEventListener('load', init);
|
||||
</script>
|
||||
</head>
|
||||
<body onbeforeunload="return blocklyFactory.confirmLeavePage()">
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Blockly Developer Tools
|
||||
<button id="helpButton" title="View documentation in new window.">
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<a href="blockfactory/index.html">
|
||||
<img src="blockfactory/devtools.png" height=96 width=184>
|
||||
<img src="blockfactory/icon.png" height=80 width=173>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Reference in New Issue
Block a user