diff --git a/blockly/blocks/arduino/procedures.js b/blockly/blocks/arduino/procedures.js index 44f245533d..7f1fc31a20 100644 --- a/blockly/blocks/arduino/procedures.js +++ b/blockly/blocks/arduino/procedures.js @@ -39,3 +39,301 @@ Blockly.Blocks['arduino_functions'] = { return true; } }; + +Blockly.Blocks['controls_effect'] = { + /** + * Block for effect condition. + * @this Blockly.Block + */ + init: function() { + var nameField = new Blockly.FieldTextInput( + Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE, + Blockly.Procedures.rename); + nameField.setSpellcheck(false); + this.arguments_ = []; + this.setHelpUrl(''); //Blockly.Msg.ARD_CONTROLS_EFFECT_HELPURL); + this.setColour(Blockly.Blocks.procedures.HUE); + this.appendValueInput('EFFECTDURATION') + .setCheck(Blockly.Types.NUMBER.checkList) + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_FIRST1) + .appendField(nameField, 'NAME') + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_FIRST2); + //this.appendStatementInput('IF0') + // .setCheck(Blockly.Types.NUMBER.checkList) + // .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_IF); + this.appendStatementInput('DO0') + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_IF); //Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_THEN); + //this.setPreviousStatement(true, 'ARD_BLOCK'); + //this.setNextStatement(true, 'ARD_BLOCK'); + if (Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT) { + this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT); + } + this.setMutator(new Blockly.Mutator(['controls_effect_elseif', + 'controls_effect_else'])); + // Assign 'this' to a variable for use in the tooltip closure below. + var thisBlock = this; + this.setTooltip(function() { + if (!thisBlock.elseifCount_ && !thisBlock.elseCount_) { + return Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_1; + } else if (!thisBlock.elseifCount_ && thisBlock.elseCount_) { + return Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_2; + } else if (thisBlock.elseifCount_ && !thisBlock.elseCount_) { + return Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_3; + } else if (thisBlock.elseifCount_ && thisBlock.elseCount_) { + return Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_4; + } + return ''; + }); + this.elseifCount_ = 0; + this.elseCount_ = 0; + }, + /** + * Initialization of the block has completed, clean up anything that may be + * inconsistent as a result of the XML loading. + * @this Blockly.Block + */ + validate: function () { + var name = Blockly.Procedures.findLegalName( + this.getFieldValue('NAME'), this); + this.setFieldValue(name, 'NAME'); + }, + /** + * Create XML to represent the number of else-if and else inputs. + * @return {Element} XML storage element. + * @this Blockly.Block + */ + mutationToDom: function() { + if (!this.elseifCount_ && !this.elseCount_) { + return null; + } + var container = document.createElement('mutation'); + if (this.elseifCount_) { + container.setAttribute('elseif', this.elseifCount_); + } + if (this.elseCount_) { + container.setAttribute('else', 1); + } + return container; + }, + /** + * Parse XML to restore the else-if and else inputs. + * @param {!Element} xmlElement XML storage element. + * @this Blockly.Block + */ + domToMutation: function(xmlElement) { + this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0; + this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0; + for (var i = 1; i <= this.elseifCount_; i++) { + this.appendValueInput('IF' + i) + .setCheck(Blockly.Types.NUMBER.checkList) + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSEIF); + this.appendStatementInput('DO' + i) + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_THEN); + } + if (this.elseCount_) { + this.appendStatementInput('ELSE') + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSE); + } + }, + /** + * Populate the mutator's dialog with this block's components. + * @param {!Blockly.Workspace} workspace Mutator's workspace. + * @return {!Blockly.Block} Root block in mutator. + * @this Blockly.Block + */ + decompose: function(workspace) { + var containerBlock = workspace.newBlock('controls_effect_if'); + containerBlock.initSvg(); + var connection = containerBlock.nextConnection; + for (var i = 1; i <= this.elseifCount_; i++) { + var elseifBlock = workspace.newBlock('controls_effect_elseif'); + elseifBlock.initSvg(); + connection.connect(elseifBlock.previousConnection); + connection = elseifBlock.nextConnection; + } + if (this.elseCount_) { + var elseBlock = workspace.newBlock('controls_effect_else'); + elseBlock.initSvg(); + connection.connect(elseBlock.previousConnection); + } + return containerBlock; + }, + /** + * Reconfigure this block based on the mutator dialog's components. + * @param {!Blockly.Block} containerBlock Root block in mutator. + * @this Blockly.Block + */ + compose: function(containerBlock) { + // Disconnect the else input blocks and remove the inputs. + if (this.elseCount_) { + this.removeInput('ELSE'); + } + this.elseCount_ = 0; + // Disconnect all the elseif input blocks and remove the inputs. + for (var i = this.elseifCount_; i > 0; i--) { + this.removeInput('IF' + i); + this.removeInput('DO' + i); + } + this.elseifCount_ = 0; + // Rebuild the block's optional inputs. + var clauseBlock = containerBlock.nextConnection.targetBlock(); + while (clauseBlock) { + switch (clauseBlock.type) { + case 'controls_effect_elseif': + this.elseifCount_++; + var ifInput = this.appendValueInput('IF' + this.elseifCount_) + .setCheck(Blockly.Types.NUMBER.checkList) + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSEIF); + var doInput = this.appendStatementInput('DO' + this.elseifCount_); + doInput.appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_THEN); + // Reconnect any child blocks. + if (clauseBlock.valueConnection_) { + ifInput.connection.connect(clauseBlock.valueConnection_); + } + if (clauseBlock.statementConnection_) { + doInput.connection.connect(clauseBlock.statementConnection_); + } + break; + case 'controls_effect_else': + this.elseCount_++; + var elseInput = this.appendStatementInput('ELSE'); + elseInput.appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSE); + // Reconnect any child blocks. + if (clauseBlock.statementConnection_) { + elseInput.connection.connect(clauseBlock.statementConnection_); + } + break; + default: + throw 'Unknown block type.'; + } + clauseBlock = clauseBlock.nextConnection && + clauseBlock.nextConnection.targetBlock(); + } + }, + /** + * Store pointers to any connected child blocks. + * @param {!Blockly.Block} containerBlock Root block in mutator. + * @this Blockly.Block + */ + saveConnections: function(containerBlock) { + var clauseBlock = containerBlock.nextConnection.targetBlock(); + var i = 1; + while (clauseBlock) { + switch (clauseBlock.type) { + case 'controls_effect_elseif': + var inputIf = this.getInput('IF' + i); + var inputDo = this.getInput('DO' + i); + clauseBlock.valueConnection_ = + inputIf && inputIf.connection.targetConnection; + clauseBlock.statementConnection_ = + inputDo && inputDo.connection.targetConnection; + i++; + break; + case 'controls_effect_else': + var inputDo = this.getInput('ELSE'); + clauseBlock.statementConnection_ = + inputDo && inputDo.connection.targetConnection; + break; + default: + throw 'Unknown block type.'; + } + clauseBlock = clauseBlock.nextConnection && + clauseBlock.nextConnection.targetBlock(); + } + }, + /** + * Dispose of any callers. + * @this Blockly.Block + */ + dispose: function() { + var name = this.getFieldValue('NAME'); + Blockly.Procedures.disposeCallers(name, this.workspace); + // Call parent's destructor. + this.constructor.prototype.dispose.apply(this, arguments); + }, + /** + * Return the signature of this procedure definition. + * @return {!Array} Tuple containing three elements: + * - the name of the defined procedure, + * - a list of all its arguments, + * - that it DOES NOT have a return value. + * @this Blockly.Block + */ + getProcedureDef: function() { + return [this.getFieldValue('NAME'), this.arguments_, false]; + }, + /** + * Return all variables referenced by this block. + * @return {!Array.} List of variable names. + * @this Blockly.Block + */ + getVars: function() { + return this.arguments_; + }, + /** + * Add custom menu options to this block's context menu. + * @param {!Array} options List of menu options to add to. + * @this Blockly.Block + */ + customContextMenu: function(options) { + // Add option to create caller. + var option = {enabled: true}; + var name = this.getFieldValue('NAME'); + option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name); + var xmlMutation = goog.dom.createDom('mutation'); + xmlMutation.setAttribute('name', name); + + var xmlBlock = goog.dom.createDom('block', null, xmlMutation); + xmlBlock.setAttribute('type', this.callType_); + option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + options.push(option); + }, + callType_: 'procedures_callnoreturn' +}; + +Blockly.Blocks['controls_effect_if'] = { + /** + * Mutator block for if container. + * @this Blockly.Block + */ + init: function() { + this.setColour(Blockly.Blocks.procedures.HUE); + this.appendDummyInput() + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_IF_TITLE_IF); + this.setNextStatement(true); + this.setTooltip(Blockly.Msg.ARD_CONTROLS_EFFECT_IF_TOOLTIP); + this.contextMenu = false; + } +}; + +Blockly.Blocks['controls_effect_elseif'] = { + /** + * Mutator bolck for else-if condition. + * @this Blockly.Block + */ + init: function() { + this.setColour(Blockly.Blocks.procedures.HUE); + this.appendDummyInput() + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_ELSEIF_TITLE_ELSEIF); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setTooltip(Blockly.Msg.ARD_CONTROLS_EFFECT_ELSEIF_TOOLTIP); + this.contextMenu = false; + } +}; + +Blockly.Blocks['controls_effect_else'] = { + /** + * Mutator block for else condition. + * @this Blockly.Block + */ + init: function() { + this.setColour(Blockly.Blocks.procedures.HUE); + this.appendDummyInput() + .appendField(Blockly.Msg.ARD_CONTROLS_EFFECT_ELSE_TITLE_ELSE); + this.setPreviousStatement(true); + this.setTooltip(Blockly.Msg.ARD_CONTROLS_EFFECT_ELSE_TOOLTIP); + this.contextMenu = false; + } +}; + diff --git a/blockly/core/procedures.js b/blockly/core/procedures.js index 3370a95fa5..121fc591c2 100644 --- a/blockly/core/procedures.js +++ b/blockly/core/procedures.js @@ -193,6 +193,12 @@ Blockly.Procedures.flyoutCategory = function(workspace) { block.setAttribute('gap', 16); xmlList.push(block); } + if (Blockly.Blocks['controls_effect']) { + var block = goog.dom.createDom('block'); + block.setAttribute('type', 'controls_effect'); + block.setAttribute('gap', 16); + xmlList.push(block); + } if (xmlList.length) { // Add slightly larger gap between system blocks and user calls. xmlList[xmlList.length - 1].setAttribute('gap', 24); diff --git a/blockly/generators/arduino/procedures.js b/blockly/generators/arduino/procedures.js index ee0de7f18e..3bdf873d1f 100644 --- a/blockly/generators/arduino/procedures.js +++ b/blockly/generators/arduino/procedures.js @@ -159,3 +159,88 @@ Blockly.Arduino['arduino_functions'] = function(block) { //var loopcode = Blockly.Arduino.scrub_(block, loopBranch); No comment block return loopBranch; }; + +/** + * Code generator to create effect statement. + * @param {!Blockly.Block} block Block to generate the code from. + * @return {string} Completed code. + */ +Blockly.Arduino['controls_effect'] = function(block) { + + var funcName = Blockly.Arduino.variableDB_.getName( + block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE); + + var duration = Blockly.Arduino.valueToCode( + block, 'EFFECTDURATION', Blockly.Arduino.ORDER_ATOMIC) || '1000'; + var effectnr = 0; + while (!(Blockly.Arduino.userFunctions_['ard_effect' + effectnr.toString()] === undefined)) { + effectnr += 1; + } + var seffectnr = effectnr.toString(); + + var declare_effect_branch = '' + +'int ard_effect' + seffectnr + '_status = -1;\n' + +'unsigned long ard_effect' + seffectnr + '_start, ard_effect' + seffectnr + '_time;\n' + +'#define EFFECT' + seffectnr + '_PERIOD ' + duration + '\n'; + var declare_effect_function = '\n' + +'void ' + funcName + '() {\n' + +' //Variables of this effect are reffered to with ard_effect' + seffectnr + '\n' + +' boolean restart = false;\n' + +' ard_effect' + seffectnr + '_time = millis() - ard_effect' + seffectnr + '_start;\n'; + + declare_effect_function += +' if (ard_effect' + seffectnr + '_time > EFFECT' + seffectnr + '_PERIOD) {\n' + +' //end effect, make sure it restarts\n' + +' if (ard_effect' + seffectnr + '_status > -1) {\n'; + + if (block.elseCount_) { + branch = Blockly.Arduino.statementToCode(block, 'ELSE'); + declare_effect_function += '' + +' //END STATEMENTS\n' + +' ' + branch; + } + + declare_effect_function += +' }\n' + +' restart = true;\n' + +' ard_effect' + seffectnr + '_status = -1;\n' + +' ard_effect' + seffectnr + '_start = ard_effect' + seffectnr + '_start + ard_effect' + seffectnr + '_time;\n' + +' ard_effect' + seffectnr + '_time = 0;\n' + +' }\n' + +' if (not restart && ard_effect' + seffectnr + '_status == -1) {\n' + +' ard_effect' + seffectnr + '_status = 0;\n' + +' ard_effect' + seffectnr + '_start = ard_effect' + seffectnr + '_start + ard_effect' + seffectnr + '_time;\n' + +' ard_effect' + seffectnr + '_time = 0;\n'; + + + var setupCode = 'ard_effect' + seffectnr + '_status = -1;\n' + + ' ard_effect' + seffectnr + '_start = millis();\n' + Blockly.Arduino.addSetup('ard_effect' + seffectnr, setupCode, false); + + var n = 0; + var branch = Blockly.Arduino.statementToCode(block, 'DO' + n); + declare_effect_function += branch + ' }\n'; + var extra = ' ' + for (n = 1; n <= block.elseifCount_; n++) { + duration = Blockly.Arduino.valueToCode(block, 'IF' + n, + Blockly.Arduino.ORDER_NONE) || duration; + branch = Blockly.Arduino.statementToCode(block, 'DO' + n); + declare_effect_branch += '#define EFFECT' + seffectnr + '_' + n + '_DURATION ' + duration + '\n'; + if (n != 1) { + extra = ' } else '; + } + declare_effect_function += extra + +'if (ard_effect' + seffectnr + '_time > EFFECT' + seffectnr + '_' + n + '_DURATION && ard_effect' + seffectnr + '_status < ' + n + ') {\n' + +' ard_effect' + seffectnr + '_status = ' + n + ';\n' + branch; + } + + //end reached of effect statements, finish up + if (n != 1) { + declare_effect_function += ' }\n' + } + declare_effect_function += '}\n'; + + var code = Blockly.Arduino.scrub_(block, declare_effect_branch + declare_effect_function); + Blockly.Arduino.userFunctions_['ard_effect' + seffectnr] = code; + return ''; +}; diff --git a/blockly/msg/json/nl.json b/blockly/msg/json/nl.json index f1fccd9b80..3aaf9f2bf0 100644 --- a/blockly/msg/json/nl.json +++ b/blockly/msg/json/nl.json @@ -329,7 +329,7 @@ "VARIABLES_SET_TOOLTIP": "Verandert de waarde van de variabele naar de waarde van de invoer.", "VARIABLES_SET_CREATE_GET": "Maak 'opvragen van %1'", "PROCEDURES_DEFNORETURN_HELPURL": "https://nl.wikipedia.org/wiki/Subprogramma", - "PROCEDURES_DEFNORETURN_TITLE": "om", + "PROCEDURES_DEFNORETURN_TITLE": "opdracht", "PROCEDURES_DEFNORETURN_PROCEDURE": "doe iets", "PROCEDURES_BEFORE_PARAMS": "met:", "PROCEDURES_CALL_BEFORE_PARAMS": "met:", @@ -463,5 +463,17 @@ "ARD_TONE_TIP": "Speelt een noot op de gegeven pin met de opgegeven frequentie binnen interval 31 - 65535", "ARD_TONE_WARNING": "Frequentie moet in het interval 31 - 65535 liggen", "ARD_NOTONE": "Stop afspelen noot op pin#", - "ARD_NOTONE_TIP": "Stopt met een noot af te spelen op de gegeven pin" + "ARD_NOTONE_TIP": "Stopt met een noot af te spelen op de gegeven pin", + "ARD_CONTROLS_EFFECT_TOOLTIP_1": "Aan de start van een effect doen we enkele opdrachten", + "ARD_CONTROLS_EFFECT_TOOLTIP_2": "Aan de start van een effect doen we enkele opdrachten, en aan het einde ook", + "ARD_CONTROLS_EFFECT_TOOLTIP_3": "Aan de start van een effect doen we enkele opdrachten, als de effect tijd groter wordt dan de gegeven waarde, worden de volgende opdrachten uitgevoerd.", + "ARD_CONTROLS_EFFECT_TOOLTIP_4": "Aan de start van een effect doen we enkele opdrachten, als de effect tijd groter wordt dan de gegeven waarde, worden de volgende opdrachten uitgevoerd. Aan het einde worden de laatste opdrachten uitgevoerd.", + "ARD_CONTROLS_EFFECT_MSG_FIRST1": "Effect", + "ARD_CONTROLS_EFFECT_MSG_FIRST2": "met totale duurtijd (ms) =", + "ARD_CONTROLS_EFFECT_MSG_IF": "bij start doe", + "ARD_CONTROLS_EFFECT_MSG_ELSEIF": "als effect tijd groter wordt dan", + "ARD_CONTROLS_EFFECT_MSG_ELSE": "op einde doe", + "ARD_CONTROLS_EFFECT_ELSEIF_TOOLTIP": "Voeg een extra effect tijd toe om opdrachten bij uit te voeren", + "ARD_CONTROLS_EFFECT_ELSE_TOOLTIP": "Voeg een blok toe voor als effect gedaan is", + } diff --git a/blockly/msg/messages.js b/blockly/msg/messages.js index 586b273740..d7b3bcdb9e 100644 --- a/blockly/msg/messages.js +++ b/blockly/msg/messages.js @@ -1212,3 +1212,22 @@ Blockly.Msg.ARD_TONE_TIP = 'Sets tone on pin to specified frequency within range Blockly.Msg.ARD_TONE_WARNING = 'Frequency must be in range 31 - 65535'; Blockly.Msg.ARD_NOTONE = 'Turn off tone on pin #'; Blockly.Msg.ARD_NOTONE_TIP = 'Turns the tone off on the selected pin'; + +//effect block +Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_1 = 'At the start of an effect, do some statements'; +Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_2 = 'At the start of an effect, do some statements, and at the end of the effect too'; +Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_3 = 'At the start of an effect, do some statements, if the effect time becomes larger than the given time, do the next statements.'; +Blockly.Msg.ARD_CONTROLS_EFFECT_TOOLTIP_4 = 'At the start of an effect, do some statements, if the effect time becomes larger than the given time, do the next statements. Ath end of the effect the final statements are done.'; +Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_FIRST1 = 'Effect'; +Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_FIRST2 = 'with total duration (ms) ='; +Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_IF = 'at the start do'; +Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSEIF = 'if effect time becomes greater than'; +Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSE = 'at the end do'; +Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_THEN = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; +Blockly.Msg.ARD_CONTROLS_EFFECT_IF_TITLE_IF = Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_IF; +Blockly.Msg.ARD_CONTROLS_EFFECT_IF_TOOLTIP = Blockly.Msg.CONTROLS_IF_IF_TOOLTIP; +Blockly.Msg.ARD_CONTROLS_EFFECT_ELSEIF_TITLE_ELSEIF = Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSEIF; +Blockly.Msg.ARD_CONTROLS_EFFECT_ELSEIF_TOOLTIP = 'Add an extra effect time at which statements must be done'; +Blockly.Msg.ARD_CONTROLS_EFFECT_ELSE_TITLE_ELSE = Blockly.Msg.ARD_CONTROLS_EFFECT_MSG_ELSE; +Blockly.Msg.ARD_CONTROLS_EFFECT_ELSE_TOOLTIP = 'Add a block for statements when the effect is finished.'; +