/// <summary> /// Unbind one or more events ev from a function call. /// </summary> /// <param name="bindData">Opaque data from bindEvent_. This list is /// emptied during the course of calling this function.</param> /// <returns>The function call.</returns> internal static Delegate unbindEvent_(JsArray <EventWrapInfo> bindData) { Node node; string name; Delegate func = null; while (bindData.Length > 0) { var bindDatum = bindData.Pop(); node = bindDatum.node; name = bindDatum.name; func = bindDatum.func; node.RemoveEventListener(name, func, false); } return(func); }
/// <summary> /// Load sounds for the given workspace. /// </summary> /// <param name="pathToMedia">The path to the media directory.</param> /// <param name="workspace">The workspace to load sounds for.</param> private static void inject_loadSounds_(string pathToMedia, WorkspaceSvg workspace) { workspace.loadAudio_(new string[] { pathToMedia + "click.mp3", pathToMedia + "click.wav", pathToMedia + "click.ogg" }, "click"); workspace.loadAudio_(new string[] { pathToMedia + "disconnect.wav", pathToMedia + "disconnect.mp3", pathToMedia + "disconnect.ogg" }, "disconnect"); workspace.loadAudio_(new string[] { pathToMedia + "delete.mp3", pathToMedia + "delete.ogg", pathToMedia + "delete.wav" }, "delete"); // Bind temporary hooks that preload the sounds. var soundBinds = new JsArray <JsArray <EventWrapInfo> >(); var unbindSounds = new Action <Event>((e) => { while (soundBinds.Length != 0) { Core.unbindEvent_((JsArray <EventWrapInfo>)soundBinds.Pop()); } workspace.preloadAudio_(); }); // These are bound on mouse/touch events with Blockly.bindEventWithChecks_, so // they restrict the touch identifier that will be recognized. But this is // really something that happens on a click, not a drag, so that's not // necessary. // Android ignores any sound not loaded as a result of a user action. soundBinds.Push( Core.bindEventWithChecks_(Document.Instance, "mousemove", null, unbindSounds, true)); soundBinds.Push( Core.bindEventWithChecks_(Document.Instance, "touchstart", null, unbindSounds, true)); }
/// <summary> /// Returns field strings and any config. /// </summary> /// <param name="block">Input block.</param> /// <returns>Field strings.</returns> private static JsArray <string> getFieldsJs_(Blockly.Block block) { var fields = new JsArray <string>(); while (block != null) { if (!block.disabled && !block.getInheritedDisabled()) { switch (block.type) { case "field_static": // Result: "hello" fields.Push(FactoryUtils.escapeString(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"))); break; case "field_number": // Result: new Blockly.FieldNumber(10, 0, 100, 1), "NUMBER" var args = new JsArray <double> { Script.ParseFloat(block.getFieldValue("VALUE")), Script.ParseFloat(block.getFieldValue("MIN")), Script.ParseFloat(block.getFieldValue("MAX")), Script.ParseFloat(block.getFieldValue("PRECISION")) }; // Remove any trailing arguments that aren't needed. if (args[3] == 0) { args.Pop(); if (args[2] == Double.PositiveInfinity) { args.Pop(); if (args[1] == Double.NegativeInfinity) { args.Pop(); } } } fields.Push("new Blockly.FieldNumber(" + args.Join(", ") + "), " + FactoryUtils.escapeString(block.getFieldValue("FIELDNAME"))); break; case "field_angle": // Result: new Blockly.FieldAngle(90), "ANGLE" fields.Push("new Blockly.FieldAngle(" + Script.ParseFloat(block.getFieldValue("ANGLE")) + "), " + FactoryUtils.escapeString(block.getFieldValue("FIELDNAME"))); break; case "field_checkbox": // Result: new Blockly.FieldCheckbox("TRUE"), "CHECK" fields.Push("new Blockly.FieldCheckbox(" + FactoryUtils.escapeString(block.getFieldValue("CHECKED")) + "), " + FactoryUtils.escapeString(block.getFieldValue("FIELDNAME"))); break; case "field_colour": // Result: new Blockly.FieldColour("#ff0000"), "COLOUR" fields.Push("new Blockly.FieldColour(" + FactoryUtils.escapeString(block.getFieldValue("COLOUR")) + "), " + FactoryUtils.escapeString(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"))); break; case "field_variable": // Result: new Blockly.FieldVariable("item"), "VAR" var varname = FactoryUtils.escapeString(block.getFieldValue("TEXT") ?? null); fields.Push("new Blockly.FieldVariable(" + varname + "), " + FactoryUtils.escapeString(block.getFieldValue("FIELDNAME"))); break; case "field_dropdown": // Result: // new Blockly.FieldDropdown([["yes", "1"], ["no", "0"]]), "TOGGLE" var options = new JsArray <string>(); for (var i = 0; i < ((FieldDropdown)block).optionCount_; i++) { options[i] = "[" + FactoryUtils.escapeString(block.getFieldValue("USER" + i)) + ", " + FactoryUtils.escapeString(block.getFieldValue("CPU" + i)) + "]"; } if (options.Length > 0) { fields.Push("new Blockly.FieldDropdown([" + options.Join(", ") + "]), " + FactoryUtils.escapeString(block.getFieldValue("FIELDNAME"))); } break; case "field_image": // Result: new Blockly.FieldImage("http://...", 80, 60) var src = FactoryUtils.escapeString(block.getFieldValue("SRC")); var width = Convert.ToDouble(block.getFieldValue("WIDTH")); var height = Convert.ToDouble(block.getFieldValue("HEIGHT")); var alt = FactoryUtils.escapeString(block.getFieldValue("ALT")); fields.Push("new Blockly.FieldImage(" + src + ", " + width + ", " + height + ", " + alt + ")"); break; } } block = block.nextConnection == null ? null : block.nextConnection.targetBlock(); } return(fields); }
/// <summary> /// Update the language code as JSON. /// </summary> /// <param name="blockType"> Name of block.</param> /// <param name="rootBlock">Factory_base block.</param> /// <returns>Generanted language code.</returns> private static string formatJson_(string blockType, Blockly.Block rootBlock) { var JS = new Dictionary <string, object>(); // Type is not used by Blockly, but may be used by a loader. JS["type"] = blockType; // Generate inputs. var message = new JsArray <string>(); var args = new JsArray <object>(); var contentsBlock = rootBlock.getInputTargetBlock("INPUTS"); Blockly.Block lastInput = null; while (contentsBlock != null) { if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) { var fields = FactoryUtils.getFieldsJson_( contentsBlock.getInputTargetBlock("FIELDS")); for (var i = 0; i < fields.Length; i++) { if (fields[i] is string str) { message.Push(str.Replace(new Regex("%", RegexOptions.Multiline), "%%")); } else { args.Push(fields[i]); message.Push("%" + args.Length); } } var input = new Dictionary <string, object>(); input.Add("type", contentsBlock.type); // Dummy inputs don't have names. Other inputs do. if (contentsBlock.type != "input_dummy") { input["name"] = contentsBlock.getFieldValue("INPUTNAME"); } var check = JSON.Parse( FactoryUtils.getOptTypesFrom(contentsBlock, "TYPE") ?? "null"); if (check != null) { input["check"] = check; } var align = contentsBlock.getFieldValue("ALIGN"); if (align != "LEFT") { input["align"] = align; } args.Push(input); message.Push("%" + args.Length); lastInput = contentsBlock; } contentsBlock = contentsBlock.nextConnection == null ? null : contentsBlock.nextConnection.targetBlock(); } // Remove last input if dummy and not empty. if (lastInput != null && lastInput.type == "input_dummy") { var fields = lastInput.getInputTargetBlock("FIELDS"); if (fields != null && FactoryUtils.getFieldsJson_(fields).Join("").Trim() != "") { var align = lastInput.getFieldValue("ALIGN"); if (align != "LEFT") { JS["lastDummyAlign0"] = align; } args.Pop(); message.Pop(); } } JS["message0"] = message.Join(" "); if (args.Length > 0) { JS["args0"] = args; } // Generate inline/external switch. if (rootBlock.getFieldValue("INLINE") == "EXT") { JS["inputsInline"] = false; } else if (rootBlock.getFieldValue("INLINE") == "INT") { JS["inputsInline"] = true; } // Generate output, or next/previous connections. switch (rootBlock.getFieldValue("CONNECTIONS")) { case "LEFT": JS["output"] = JSON.Parse( FactoryUtils.getOptTypesFrom(rootBlock, "OUTPUTTYPE") ?? "null"); break; case "BOTH": JS["previousStatement"] = JSON.Parse( FactoryUtils.getOptTypesFrom(rootBlock, "TOPTYPE") ?? "null"); JS["nextStatement"] = JSON.Parse( FactoryUtils.getOptTypesFrom(rootBlock, "BOTTOMTYPE") ?? "null"); break; case "TOP": JS["previousStatement"] = JSON.Parse( FactoryUtils.getOptTypesFrom(rootBlock, "TOPTYPE") ?? "null"); break; case "BOTTOM": JS["nextStatement"] = JSON.Parse( FactoryUtils.getOptTypesFrom(rootBlock, "BOTTOMTYPE") ?? "null"); break; } // Generate colour. var colourBlock = rootBlock.getInputTargetBlock("COLOUR"); if (colourBlock != null && !colourBlock.disabled) { var hue = Script.ParseFloat(colourBlock.getFieldValue("HUE")); JS["colour"] = hue; } JS["tooltip"] = ""; JS["helpUrl"] = "http://www.example.com/"; return(JSON.Stringify(JS, null, " ")); }