pxt-core 7.5.7 → 7.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/pxt.js +1004 -4
- package/built/pxtblockly.js +432 -52
- package/built/pxtblocks.d.ts +34 -0
- package/built/pxtblocks.js +432 -52
- package/built/pxtlib.d.ts +20 -2
- package/built/pxtlib.js +127 -3
- package/built/pxtsim.d.ts +222 -0
- package/built/pxtsim.js +877 -1
- package/built/target.js +1 -1
- package/built/web/icons.css +49 -10
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtblockly.js +1 -1
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtsim.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/react-common-authcode.css +104 -1
- package/built/web/react-common-skillmap.css +1 -1
- package/built/web/rtlreact-common-skillmap.css +1 -1
- package/built/web/rtlsemantic.css +2 -2
- package/built/web/semantic.css +2 -2
- package/built/web/skillmap/js/main.b5f3628d.chunk.js +1 -0
- package/docfiles/footer.html +1 -1
- package/docfiles/script.html +1 -1
- package/docfiles/thin-footer.html +1 -1
- package/localtypings/pxtarget.d.ts +1 -0
- package/package.json +1 -1
- package/react-common/components/controls/Button.tsx +7 -3
- package/react-common/components/controls/DraggableGraph.tsx +242 -0
- package/react-common/components/controls/Dropdown.tsx +121 -0
- package/react-common/components/controls/FocusList.tsx +17 -8
- package/react-common/components/controls/Input.tsx +13 -3
- package/react-common/components/controls/RadioButtonGroup.tsx +66 -0
- package/react-common/components/util.tsx +23 -0
- package/react-common/styles/controls/DraggableGraph.less +13 -0
- package/react-common/styles/controls/Dropdown.less +64 -0
- package/react-common/styles/controls/RadioButtonGroup.less +36 -0
- package/react-common/styles/react-common-variables.less +24 -0
- package/react-common/styles/react-common.less +3 -0
- package/theme/pxt.less +1 -0
- package/theme/soundeffecteditor.less +132 -0
- package/webapp/public/skillmap.html +1 -1
- package/built/web/skillmap/js/main.2485091f.chunk.js +0 -1
package/built/pxtblockly.js
CHANGED
|
@@ -4016,7 +4016,7 @@ var pxt;
|
|
|
4016
4016
|
const call = e.stdCallTable[b.type];
|
|
4017
4017
|
if (call.attrs.shim === "ENUM_GET" || call.attrs.shim === "KIND_GET")
|
|
4018
4018
|
return;
|
|
4019
|
-
visibleParams(call, countOptionals(b)).forEach((p, i) => {
|
|
4019
|
+
visibleParams(call, countOptionals(b, call)).forEach((p, i) => {
|
|
4020
4020
|
const isInstance = call.isExtensionMethod && i === 0;
|
|
4021
4021
|
if (p.definitionName && !b.getFieldValue(p.definitionName)) {
|
|
4022
4022
|
let i = b.inputList.find((i) => i.name == p.definitionName);
|
|
@@ -4484,7 +4484,7 @@ var pxt;
|
|
|
4484
4484
|
let call = e.stdCallTable[b.type];
|
|
4485
4485
|
if (call) {
|
|
4486
4486
|
if (call.imageLiteral)
|
|
4487
|
-
expr = compileImage(e, b, call.imageLiteral, call.imageLiteralColumns, call.imageLiteralRows, call.namespace, call.f, visibleParams(call, countOptionals(b)).map(ar => compileArgument(e, b, ar, comments)));
|
|
4487
|
+
expr = compileImage(e, b, call.imageLiteral, call.imageLiteralColumns, call.imageLiteralRows, call.namespace, call.f, visibleParams(call, countOptionals(b, call)).map(ar => compileArgument(e, b, ar, comments)));
|
|
4488
4488
|
else
|
|
4489
4489
|
expr = compileStdCall(e, b, call, comments);
|
|
4490
4490
|
}
|
|
@@ -4697,12 +4697,12 @@ var pxt;
|
|
|
4697
4697
|
return blocks_1.mkStmt(blocks_1.mkInfix(ref, "+=", expr));
|
|
4698
4698
|
}
|
|
4699
4699
|
function eventArgs(call, b) {
|
|
4700
|
-
return visibleParams(call, countOptionals(b)).filter(ar => !!ar.definitionName);
|
|
4700
|
+
return visibleParams(call, countOptionals(b, call)).filter(ar => !!ar.definitionName);
|
|
4701
4701
|
}
|
|
4702
4702
|
function compileCall(e, b, comments) {
|
|
4703
4703
|
const call = e.stdCallTable[b.type];
|
|
4704
4704
|
if (call.imageLiteral)
|
|
4705
|
-
return blocks_1.mkStmt(compileImage(e, b, call.imageLiteral, call.imageLiteralColumns, call.imageLiteralRows, call.namespace, call.f, visibleParams(call, countOptionals(b)).map(ar => compileArgument(e, b, ar, comments))));
|
|
4705
|
+
return blocks_1.mkStmt(compileImage(e, b, call.imageLiteral, call.imageLiteralColumns, call.imageLiteralRows, call.namespace, call.f, visibleParams(call, countOptionals(b, call)).map(ar => compileArgument(e, b, ar, comments))));
|
|
4706
4706
|
else if (call.hasHandler)
|
|
4707
4707
|
return compileEvent(e, b, call, eventArgs(call, b), call.namespace, comments);
|
|
4708
4708
|
else
|
|
@@ -4781,7 +4781,7 @@ var pxt;
|
|
|
4781
4781
|
return blocks_1.H.mkPropertyAccess(b.getFieldValue("MEMBER"), blocks_1.mkText(info.name));
|
|
4782
4782
|
}
|
|
4783
4783
|
else {
|
|
4784
|
-
args = visibleParams(func, countOptionals(b)).map((p, i) => compileArgument(e, b, p, comments, func.isExtensionMethod && i === 0 && !func.isExpression));
|
|
4784
|
+
args = visibleParams(func, countOptionals(b, func)).map((p, i) => compileArgument(e, b, p, comments, func.isExtensionMethod && i === 0 && !func.isExpression));
|
|
4785
4785
|
}
|
|
4786
4786
|
let callNamespace = func.namespace;
|
|
4787
4787
|
let callName = func.f;
|
|
@@ -5435,7 +5435,14 @@ var pxt;
|
|
|
5435
5435
|
}
|
|
5436
5436
|
return blocks_1.mkStmt(blocks_1.mkText("let " + v.escapedName + tp + " = "), defl);
|
|
5437
5437
|
}
|
|
5438
|
-
function countOptionals(b) {
|
|
5438
|
+
function countOptionals(b, func) {
|
|
5439
|
+
if (func.attrs.compileHiddenArguments) {
|
|
5440
|
+
return func.comp.parameters.reduce((prev, block) => {
|
|
5441
|
+
if (block.isOptional)
|
|
5442
|
+
prev++;
|
|
5443
|
+
return prev;
|
|
5444
|
+
}, 0);
|
|
5445
|
+
}
|
|
5439
5446
|
if (b.mutationToDom) {
|
|
5440
5447
|
const el = b.mutationToDom();
|
|
5441
5448
|
if (el.hasAttribute("_expanded")) {
|
|
@@ -5954,6 +5961,7 @@ var pxt;
|
|
|
5954
5961
|
registerFieldEditor('protractor', pxtblockly.FieldProtractor);
|
|
5955
5962
|
registerFieldEditor('position', pxtblockly.FieldPosition);
|
|
5956
5963
|
registerFieldEditor('melody', pxtblockly.FieldCustomMelody);
|
|
5964
|
+
registerFieldEditor('soundeffect', pxtblockly.FieldSoundEffect);
|
|
5957
5965
|
}
|
|
5958
5966
|
blocks.initFieldEditors = initFieldEditors;
|
|
5959
5967
|
function registerFieldEditor(selector, field, validator) {
|
|
@@ -9833,9 +9841,11 @@ var pxt;
|
|
|
9833
9841
|
return pxt.Util.values(apis.byQName).filter(sym => sym.namespace === enumName && !sym.attributes.blockHidden);
|
|
9834
9842
|
}
|
|
9835
9843
|
function getFixedInstanceDropdownValues(apis, qName) {
|
|
9836
|
-
|
|
9844
|
+
const symbols = pxt.Util.values(apis.byQName).filter(sym => sym.kind === 4 /* Variable */
|
|
9837
9845
|
&& sym.attributes.fixedInstance
|
|
9838
|
-
&& isSubtype(apis, sym.retType, qName))
|
|
9846
|
+
&& isSubtype(apis, sym.retType, qName))
|
|
9847
|
+
.sort((l, r) => (r.attributes.weight || 50) - (l.attributes.weight || 50));
|
|
9848
|
+
return symbols;
|
|
9839
9849
|
}
|
|
9840
9850
|
blocks_4.getFixedInstanceDropdownValues = getFixedInstanceDropdownValues;
|
|
9841
9851
|
function generateIcons(instanceSymbols) {
|
|
@@ -10864,18 +10874,21 @@ var pxt;
|
|
|
10864
10874
|
// by BlocklyLoader. The number makes it an invalid JS identifier
|
|
10865
10875
|
const buttonAddName = "0_add_button";
|
|
10866
10876
|
const buttonRemName = "0_rem_button";
|
|
10877
|
+
const buttonAddRemName = "0_add_rem_button";
|
|
10867
10878
|
const numVisibleAttr = "_expanded";
|
|
10868
10879
|
const inputInitAttr = "_input_init";
|
|
10869
10880
|
const optionNames = def.parameters.map(p => p.name);
|
|
10870
10881
|
const totalOptions = def.parameters.length;
|
|
10871
10882
|
const buttonDelta = toggle ? totalOptions : 1;
|
|
10883
|
+
const variableInlineInputs = info.blocksById[b.type].attributes.inlineInputMode === "variable";
|
|
10884
|
+
const compileHiddenArguments = info.blocksById[b.type].attributes.compileHiddenArguments;
|
|
10872
10885
|
const state = new MutationState(b);
|
|
10873
10886
|
state.setEventsEnabled(false);
|
|
10874
10887
|
state.setValue(numVisibleAttr, 0);
|
|
10875
10888
|
state.setValue(inputInitAttr, false);
|
|
10876
10889
|
state.setEventsEnabled(true);
|
|
10877
10890
|
Blockly.Extensions.apply('inline-svgs', b, false);
|
|
10878
|
-
|
|
10891
|
+
let updatingInputs = false;
|
|
10879
10892
|
appendMutation(b, {
|
|
10880
10893
|
mutationToDom: (el) => {
|
|
10881
10894
|
// The reason we store the inputsInitialized variable separately from visibleOptions
|
|
@@ -10890,8 +10903,8 @@ var pxt;
|
|
|
10890
10903
|
state.setEventsEnabled(false);
|
|
10891
10904
|
if (saved.hasAttribute(inputInitAttr) && saved.getAttribute(inputInitAttr) == "true" && !state.getBoolean(inputInitAttr)) {
|
|
10892
10905
|
state.setValue(inputInitAttr, true);
|
|
10893
|
-
initOptionalInputs();
|
|
10894
10906
|
}
|
|
10907
|
+
initOptionalInputs();
|
|
10895
10908
|
if (saved.hasAttribute(numVisibleAttr)) {
|
|
10896
10909
|
const val = parseInt(saved.getAttribute(numVisibleAttr));
|
|
10897
10910
|
if (!isNaN(val)) {
|
|
@@ -10902,6 +10915,7 @@ var pxt;
|
|
|
10902
10915
|
}
|
|
10903
10916
|
else {
|
|
10904
10917
|
state.setValue(numVisibleAttr, addDelta(delta));
|
|
10918
|
+
updateButtons();
|
|
10905
10919
|
}
|
|
10906
10920
|
}
|
|
10907
10921
|
else {
|
|
@@ -10912,14 +10926,29 @@ var pxt;
|
|
|
10912
10926
|
state.setEventsEnabled(true);
|
|
10913
10927
|
}
|
|
10914
10928
|
});
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10929
|
+
initOptionalInputs();
|
|
10930
|
+
if (compileHiddenArguments) {
|
|
10931
|
+
// Make sure all inputs have shadow blocks attached
|
|
10932
|
+
let optIndex = 0;
|
|
10933
|
+
for (let i = 0; i < b.inputList.length; i++) {
|
|
10934
|
+
const input = b.inputList[i];
|
|
10935
|
+
if (pxt.Util.startsWith(input.name, blocks.optionalInputWithFieldPrefix) || optionNames.indexOf(input.name) !== -1) {
|
|
10936
|
+
if (input.connection && !input.connection.isConnected() && !b.isInsertionMarker()) {
|
|
10937
|
+
const param = comp.definitionNameToParam[def.parameters[optIndex].name];
|
|
10938
|
+
attachShadowBlock(input, param);
|
|
10939
|
+
}
|
|
10940
|
+
++optIndex;
|
|
10941
|
+
}
|
|
10921
10942
|
}
|
|
10922
|
-
}
|
|
10943
|
+
}
|
|
10944
|
+
b.render = (opt_bubble) => {
|
|
10945
|
+
if (updatingInputs)
|
|
10946
|
+
return;
|
|
10947
|
+
updatingInputs = true;
|
|
10948
|
+
updateShape(0, undefined, true);
|
|
10949
|
+
updatingInputs = false;
|
|
10950
|
+
Blockly.BlockSvg.prototype.render.call(b, opt_bubble);
|
|
10951
|
+
};
|
|
10923
10952
|
// Set skipRender to true if the block is still initializing. Otherwise
|
|
10924
10953
|
// the inputs will render before their shadow blocks are created and
|
|
10925
10954
|
// leave behind annoying artifacts
|
|
@@ -10949,25 +10978,14 @@ var pxt;
|
|
|
10949
10978
|
setInputVisible(input, visible);
|
|
10950
10979
|
if (visible && input.connection && !input.connection.isConnected() && !b.isInsertionMarker()) {
|
|
10951
10980
|
const param = comp.definitionNameToParam[def.parameters[optIndex].name];
|
|
10952
|
-
|
|
10953
|
-
if (shadow.tagName.toLowerCase() === "value") {
|
|
10954
|
-
// Unwrap the block
|
|
10955
|
-
shadow = shadow.firstElementChild;
|
|
10956
|
-
}
|
|
10957
|
-
Blockly.Events.disable();
|
|
10958
|
-
try {
|
|
10959
|
-
const nb = Blockly.Xml.domToBlock(shadow, b.workspace);
|
|
10960
|
-
if (nb) {
|
|
10961
|
-
input.connection.connect(nb.outputConnection);
|
|
10962
|
-
}
|
|
10963
|
-
}
|
|
10964
|
-
catch (e) { }
|
|
10965
|
-
Blockly.Events.enable();
|
|
10981
|
+
attachShadowBlock(input, param);
|
|
10966
10982
|
}
|
|
10967
10983
|
++optIndex;
|
|
10968
10984
|
}
|
|
10969
10985
|
}
|
|
10970
10986
|
updateButtons();
|
|
10987
|
+
if (variableInlineInputs)
|
|
10988
|
+
b.setInputsInline(visibleOptions < 4);
|
|
10971
10989
|
if (!skipRender)
|
|
10972
10990
|
b.render();
|
|
10973
10991
|
}
|
|
@@ -10976,31 +10994,32 @@ var pxt;
|
|
|
10976
10994
|
.appendField(new Blockly.FieldImage(uri, 24, 24, alt, () => updateShape(delta), false));
|
|
10977
10995
|
}
|
|
10978
10996
|
function updateButtons() {
|
|
10997
|
+
if (updatingInputs)
|
|
10998
|
+
return;
|
|
10979
10999
|
const visibleOptions = state.getNumber(numVisibleAttr);
|
|
10980
11000
|
const showPlus = visibleOptions !== totalOptions;
|
|
10981
11001
|
const showMinus = visibleOptions !== 0;
|
|
10982
|
-
|
|
10983
|
-
const hasPlus = !!b.getInput(buttonAddName);
|
|
10984
|
-
if (!showPlus) {
|
|
11002
|
+
if (b.inputList.some(i => i.name === buttonAddName))
|
|
10985
11003
|
b.removeInput(buttonAddName, true);
|
|
10986
|
-
|
|
10987
|
-
if (!showMinus) {
|
|
11004
|
+
if (b.inputList.some(i => i.name === buttonRemName))
|
|
10988
11005
|
b.removeInput(buttonRemName, true);
|
|
11006
|
+
if (b.inputList.some(i => i.name === buttonAddRemName))
|
|
11007
|
+
b.removeInput(buttonAddRemName, true);
|
|
11008
|
+
if (showPlus && showMinus) {
|
|
11009
|
+
addPlusAndMinusButtons();
|
|
10989
11010
|
}
|
|
10990
|
-
if (
|
|
10991
|
-
|
|
11011
|
+
else if (showPlus) {
|
|
11012
|
+
addPlusButton();
|
|
10992
11013
|
}
|
|
10993
|
-
if (
|
|
10994
|
-
|
|
10995
|
-
if (hasPlus && b.inputList.findIndex(el => el.name === buttonAddName) !== b.inputList.length - 1) {
|
|
10996
|
-
b.removeInput(buttonAddName, true);
|
|
10997
|
-
addPlusButton();
|
|
10998
|
-
}
|
|
10999
|
-
else if (!hasPlus) {
|
|
11000
|
-
addPlusButton();
|
|
11001
|
-
}
|
|
11014
|
+
else if (showMinus) {
|
|
11015
|
+
addMinusButton();
|
|
11002
11016
|
}
|
|
11003
11017
|
}
|
|
11018
|
+
function addPlusAndMinusButtons() {
|
|
11019
|
+
b.appendDummyInput(buttonAddRemName)
|
|
11020
|
+
.appendField(new Blockly.FieldImage(b.REMOVE_IMAGE_DATAURI, 24, 24, lf("Hide optional arguments"), () => updateShape(-1 * buttonDelta), false))
|
|
11021
|
+
.appendField(new Blockly.FieldImage(b.ADD_IMAGE_DATAURI, 24, 24, lf("Reveal optional arguments"), () => updateShape(buttonDelta), false));
|
|
11022
|
+
}
|
|
11004
11023
|
function addPlusButton() {
|
|
11005
11024
|
addButton(buttonAddName, b.ADD_IMAGE_DATAURI, lf("Reveal optional arguments"), buttonDelta);
|
|
11006
11025
|
}
|
|
@@ -11017,12 +11036,23 @@ var pxt;
|
|
|
11017
11036
|
}
|
|
11018
11037
|
function setInputVisible(input, visible) {
|
|
11019
11038
|
// If the block isn't rendered, Blockly will crash
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11039
|
+
input.setVisible(visible);
|
|
11040
|
+
}
|
|
11041
|
+
function attachShadowBlock(input, param) {
|
|
11042
|
+
let shadow = blocks.createShadowValue(info, param);
|
|
11043
|
+
if (shadow.tagName.toLowerCase() === "value") {
|
|
11044
|
+
// Unwrap the block
|
|
11045
|
+
shadow = shadow.firstElementChild;
|
|
11046
|
+
}
|
|
11047
|
+
Blockly.Events.disable();
|
|
11048
|
+
try {
|
|
11049
|
+
const nb = Blockly.Xml.domToBlock(shadow, b.workspace);
|
|
11050
|
+
if (nb) {
|
|
11051
|
+
input.connection.connect(nb.outputConnection);
|
|
11052
|
+
}
|
|
11025
11053
|
}
|
|
11054
|
+
catch (e) { }
|
|
11055
|
+
Blockly.Events.enable();
|
|
11026
11056
|
}
|
|
11027
11057
|
}
|
|
11028
11058
|
blocks.initExpandableBlock = initExpandableBlock;
|
|
@@ -11362,6 +11392,17 @@ var pxtblockly;
|
|
|
11362
11392
|
this.loaded = true;
|
|
11363
11393
|
this.valueText = this.onValueChanged(this.valueText);
|
|
11364
11394
|
}
|
|
11395
|
+
getAnchorDimensions() {
|
|
11396
|
+
const boundingBox = this.getScaledBBox();
|
|
11397
|
+
if (this.sourceBlock_.RTL) {
|
|
11398
|
+
boundingBox.right += Blockly.FieldDropdown.CHECKMARK_OVERHANG;
|
|
11399
|
+
}
|
|
11400
|
+
else {
|
|
11401
|
+
boundingBox.left -= Blockly.FieldDropdown.CHECKMARK_OVERHANG;
|
|
11402
|
+
}
|
|
11403
|
+
return boundingBox;
|
|
11404
|
+
}
|
|
11405
|
+
;
|
|
11365
11406
|
isInitialized() {
|
|
11366
11407
|
return !!this.fieldGroup_;
|
|
11367
11408
|
}
|
|
@@ -11371,6 +11412,23 @@ var pxtblockly;
|
|
|
11371
11412
|
setBlockData(value) {
|
|
11372
11413
|
pxt.blocks.setBlockDataForField(this.sourceBlock_, this.name, value);
|
|
11373
11414
|
}
|
|
11415
|
+
getSiblingBlock(inputName, useGrandparent = false) {
|
|
11416
|
+
const block = useGrandparent ? this.sourceBlock_.parentBlock_ : this.sourceBlock_;
|
|
11417
|
+
if (!block || !block.inputList)
|
|
11418
|
+
return undefined;
|
|
11419
|
+
for (const input of block.inputList) {
|
|
11420
|
+
if (input.name === inputName) {
|
|
11421
|
+
return input.connection.targetBlock();
|
|
11422
|
+
}
|
|
11423
|
+
}
|
|
11424
|
+
return undefined;
|
|
11425
|
+
}
|
|
11426
|
+
getSiblingField(fieldName, useGrandparent = false) {
|
|
11427
|
+
const block = useGrandparent ? this.sourceBlock_.parentBlock_ : this.sourceBlock_;
|
|
11428
|
+
if (!block)
|
|
11429
|
+
return undefined;
|
|
11430
|
+
return block.getField(fieldName);
|
|
11431
|
+
}
|
|
11374
11432
|
}
|
|
11375
11433
|
pxtblockly.FieldBase = FieldBase;
|
|
11376
11434
|
})(pxtblockly || (pxtblockly = {}));
|
|
@@ -15073,6 +15131,311 @@ var pxtblockly;
|
|
|
15073
15131
|
}
|
|
15074
15132
|
pxtblockly.FieldProtractor = FieldProtractor;
|
|
15075
15133
|
})(pxtblockly || (pxtblockly = {}));
|
|
15134
|
+
/// <reference path="../../built/pxtlib.d.ts" />
|
|
15135
|
+
/// <reference path="./field_base.ts" />
|
|
15136
|
+
var pxtblockly;
|
|
15137
|
+
(function (pxtblockly) {
|
|
15138
|
+
var svg = pxt.svgUtil;
|
|
15139
|
+
const MUSIC_ICON_WIDTH = 20;
|
|
15140
|
+
const TOTAL_WIDTH = 160;
|
|
15141
|
+
const TOTAL_HEIGHT = 40;
|
|
15142
|
+
const X_PADDING = 5;
|
|
15143
|
+
const Y_PADDING = 4;
|
|
15144
|
+
const PREVIEW_WIDTH = TOTAL_WIDTH - X_PADDING * 5 - MUSIC_ICON_WIDTH;
|
|
15145
|
+
class FieldSoundEffect extends pxtblockly.FieldBase {
|
|
15146
|
+
onInit() {
|
|
15147
|
+
if (!this.options)
|
|
15148
|
+
this.options = {};
|
|
15149
|
+
if (!this.options.durationInputName)
|
|
15150
|
+
this.options.durationInputName = "duration";
|
|
15151
|
+
if (!this.options.startFrequencyInputName)
|
|
15152
|
+
this.options.startFrequencyInputName = "startFrequency";
|
|
15153
|
+
if (!this.options.endFrequencyInputName)
|
|
15154
|
+
this.options.endFrequencyInputName = "endFrequency";
|
|
15155
|
+
if (!this.options.startVolumeInputName)
|
|
15156
|
+
this.options.startVolumeInputName = "startVolume";
|
|
15157
|
+
if (!this.options.endVolumeInputName)
|
|
15158
|
+
this.options.endVolumeInputName = "endVolume";
|
|
15159
|
+
if (!this.options.waveFieldName)
|
|
15160
|
+
this.options.waveFieldName = "waveShape";
|
|
15161
|
+
if (!this.options.interpolationFieldName)
|
|
15162
|
+
this.options.interpolationFieldName = "interpolation";
|
|
15163
|
+
if (!this.options.effectFieldName)
|
|
15164
|
+
this.options.effectFieldName = "effect";
|
|
15165
|
+
this.redrawPreview();
|
|
15166
|
+
}
|
|
15167
|
+
onDispose() {
|
|
15168
|
+
}
|
|
15169
|
+
onValueChanged(newValue) {
|
|
15170
|
+
return newValue;
|
|
15171
|
+
}
|
|
15172
|
+
redrawPreview() {
|
|
15173
|
+
if (!this.fieldGroup_)
|
|
15174
|
+
return;
|
|
15175
|
+
pxsim.U.clear(this.fieldGroup_);
|
|
15176
|
+
const bg = new svg.Rect()
|
|
15177
|
+
.at(X_PADDING, Y_PADDING)
|
|
15178
|
+
.size(TOTAL_WIDTH, TOTAL_HEIGHT)
|
|
15179
|
+
.setClass("blocklySpriteField")
|
|
15180
|
+
.stroke("#fff", 1)
|
|
15181
|
+
.corner(TOTAL_HEIGHT / 2);
|
|
15182
|
+
const clipPathId = "preview-clip-" + pxt.U.guidGen();
|
|
15183
|
+
const clip = new svg.ClipPath()
|
|
15184
|
+
.id(clipPathId)
|
|
15185
|
+
.clipPathUnits(false);
|
|
15186
|
+
const clipRect = new svg.Rect()
|
|
15187
|
+
.size(PREVIEW_WIDTH, TOTAL_HEIGHT)
|
|
15188
|
+
.fill("#FFF")
|
|
15189
|
+
.at(0, 0);
|
|
15190
|
+
clip.appendChild(clipRect);
|
|
15191
|
+
const path = new svg.Path()
|
|
15192
|
+
.stroke("grey", 2)
|
|
15193
|
+
.fill("none")
|
|
15194
|
+
.setD(pxt.assets.renderSoundPath(this.readCurrentSound(), TOTAL_WIDTH - X_PADDING * 4 - MUSIC_ICON_WIDTH, TOTAL_HEIGHT - Y_PADDING * 2))
|
|
15195
|
+
.clipPath("url('#" + clipPathId + "')");
|
|
15196
|
+
const g = new svg.Group()
|
|
15197
|
+
.translate(MUSIC_ICON_WIDTH + X_PADDING * 3, Y_PADDING + 3);
|
|
15198
|
+
g.appendChild(clip);
|
|
15199
|
+
g.appendChild(path);
|
|
15200
|
+
const musicIcon = new svg.Text("\uf001")
|
|
15201
|
+
.appendClass("melody-editor-field-icon")
|
|
15202
|
+
.setAttribute("alignment-baseline", "middle")
|
|
15203
|
+
.anchor("middle")
|
|
15204
|
+
.at(X_PADDING * 2 + MUSIC_ICON_WIDTH / 2, TOTAL_HEIGHT / 2 + 4);
|
|
15205
|
+
this.fieldGroup_.appendChild(bg.el);
|
|
15206
|
+
this.fieldGroup_.appendChild(musicIcon.el);
|
|
15207
|
+
this.fieldGroup_.appendChild(g.el);
|
|
15208
|
+
}
|
|
15209
|
+
showEditor_() {
|
|
15210
|
+
const initialSound = this.readCurrentSound();
|
|
15211
|
+
Blockly.Events.disable();
|
|
15212
|
+
Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, () => {
|
|
15213
|
+
fv.hide();
|
|
15214
|
+
widgetDiv.classList.remove("sound-effect-editor-widget");
|
|
15215
|
+
widgetDiv.style.transform = "";
|
|
15216
|
+
widgetDiv.style.position = "";
|
|
15217
|
+
widgetDiv.style.left = "";
|
|
15218
|
+
widgetDiv.style.top = "";
|
|
15219
|
+
widgetDiv.style.width = "";
|
|
15220
|
+
widgetDiv.style.height = "";
|
|
15221
|
+
widgetDiv.style.opacity = "";
|
|
15222
|
+
widgetDiv.style.transition = "";
|
|
15223
|
+
Blockly.Events.enable();
|
|
15224
|
+
Blockly.Events.setGroup(true);
|
|
15225
|
+
this.fireNumberInputUpdate(this.options.durationInputName, initialSound.duration);
|
|
15226
|
+
this.fireNumberInputUpdate(this.options.startFrequencyInputName, initialSound.startFrequency);
|
|
15227
|
+
this.fireNumberInputUpdate(this.options.endFrequencyInputName, initialSound.endFrequency);
|
|
15228
|
+
this.fireNumberInputUpdate(this.options.startVolumeInputName, initialSound.startVolume);
|
|
15229
|
+
this.fireNumberInputUpdate(this.options.endVolumeInputName, initialSound.endVolume);
|
|
15230
|
+
this.fireFieldDropdownUpdate(this.options.waveFieldName, waveformMapping[initialSound.wave]);
|
|
15231
|
+
this.fireFieldDropdownUpdate(this.options.interpolationFieldName, interpolationMapping[initialSound.interpolation]);
|
|
15232
|
+
this.fireFieldDropdownUpdate(this.options.effectFieldName, effectMapping[initialSound.effect]);
|
|
15233
|
+
Blockly.Events.setGroup(false);
|
|
15234
|
+
if (this.mostRecentValue)
|
|
15235
|
+
this.setBlockData(JSON.stringify(this.mostRecentValue));
|
|
15236
|
+
});
|
|
15237
|
+
const widgetDiv = Blockly.WidgetDiv.DIV;
|
|
15238
|
+
const opts = {
|
|
15239
|
+
onClose: () => {
|
|
15240
|
+
fv.hide();
|
|
15241
|
+
Blockly.WidgetDiv.hideIfOwner(this);
|
|
15242
|
+
},
|
|
15243
|
+
onSoundChange: (newSound) => {
|
|
15244
|
+
this.mostRecentValue = newSound;
|
|
15245
|
+
this.updateSiblingBlocks(newSound);
|
|
15246
|
+
this.redrawPreview();
|
|
15247
|
+
},
|
|
15248
|
+
initialSound: initialSound
|
|
15249
|
+
};
|
|
15250
|
+
const fv = pxt.react.getFieldEditorView("soundeffect-editor", initialSound, opts, widgetDiv);
|
|
15251
|
+
const block = this.sourceBlock_;
|
|
15252
|
+
const bounds = block.getBoundingRectangle();
|
|
15253
|
+
const coord = pxtblockly.workspaceToScreenCoordinates(block.workspace, new Blockly.utils.Coordinate(bounds.right, bounds.top));
|
|
15254
|
+
const animationDistance = 20;
|
|
15255
|
+
const left = coord.x + 20;
|
|
15256
|
+
const top = coord.y - animationDistance;
|
|
15257
|
+
widgetDiv.style.opacity = "0";
|
|
15258
|
+
widgetDiv.classList.add("sound-effect-editor-widget");
|
|
15259
|
+
widgetDiv.style.position = "absolute";
|
|
15260
|
+
widgetDiv.style.left = left + "px";
|
|
15261
|
+
widgetDiv.style.top = top + "px";
|
|
15262
|
+
widgetDiv.style.width = "30rem";
|
|
15263
|
+
widgetDiv.style.height = "40rem";
|
|
15264
|
+
widgetDiv.style.display = "block";
|
|
15265
|
+
widgetDiv.style.transition = "transform 0.25s ease 0s, opacity 0.25s ease 0s";
|
|
15266
|
+
fv.onHide(() => {
|
|
15267
|
+
// do nothing
|
|
15268
|
+
});
|
|
15269
|
+
fv.show();
|
|
15270
|
+
const divBounds = widgetDiv.getBoundingClientRect();
|
|
15271
|
+
const injectDivBounds = block.workspace.getInjectionDiv().getBoundingClientRect();
|
|
15272
|
+
if (divBounds.height > injectDivBounds.height) {
|
|
15273
|
+
widgetDiv.style.height = "";
|
|
15274
|
+
widgetDiv.style.top = `calc(1rem - ${animationDistance}px)`;
|
|
15275
|
+
widgetDiv.style.bottom = "1rem";
|
|
15276
|
+
}
|
|
15277
|
+
else {
|
|
15278
|
+
if (divBounds.bottom > injectDivBounds.bottom || divBounds.top < injectDivBounds.top) {
|
|
15279
|
+
// This editor is pretty tall, so just center vertically on the inject div
|
|
15280
|
+
widgetDiv.style.top = (injectDivBounds.top + (injectDivBounds.height / 2) - (divBounds.height / 2)) - animationDistance + "px";
|
|
15281
|
+
}
|
|
15282
|
+
}
|
|
15283
|
+
if (divBounds.width > injectDivBounds.width) {
|
|
15284
|
+
widgetDiv.style.width = "";
|
|
15285
|
+
widgetDiv.style.left = "1rem";
|
|
15286
|
+
widgetDiv.style.right = "1rem";
|
|
15287
|
+
}
|
|
15288
|
+
else {
|
|
15289
|
+
// Check to see if we are bleeding off the right side of the canvas
|
|
15290
|
+
if (divBounds.left + divBounds.width >= injectDivBounds.right) {
|
|
15291
|
+
// If so, try and place to the left of the block instead of the right
|
|
15292
|
+
const blockLeft = pxtblockly.workspaceToScreenCoordinates(block.workspace, new Blockly.utils.Coordinate(bounds.left, bounds.top));
|
|
15293
|
+
const toolboxWidth = block.workspace.getToolbox().getWidth();
|
|
15294
|
+
const workspaceLeft = injectDivBounds.left + toolboxWidth;
|
|
15295
|
+
if (blockLeft.x - divBounds.width - 20 > workspaceLeft) {
|
|
15296
|
+
widgetDiv.style.left = (blockLeft.x - divBounds.width - 20) + "px";
|
|
15297
|
+
}
|
|
15298
|
+
else {
|
|
15299
|
+
// As a last resort, just center on the inject div
|
|
15300
|
+
widgetDiv.style.left = (workspaceLeft + ((injectDivBounds.width - toolboxWidth) / 2) - divBounds.width / 2) + "px";
|
|
15301
|
+
}
|
|
15302
|
+
}
|
|
15303
|
+
}
|
|
15304
|
+
requestAnimationFrame(() => {
|
|
15305
|
+
widgetDiv.style.opacity = "1";
|
|
15306
|
+
widgetDiv.style.transform = `translateY(${animationDistance}px)`;
|
|
15307
|
+
});
|
|
15308
|
+
}
|
|
15309
|
+
render_() {
|
|
15310
|
+
super.render_();
|
|
15311
|
+
this.size_.height = TOTAL_HEIGHT + Y_PADDING * 2;
|
|
15312
|
+
this.size_.width = TOTAL_WIDTH;
|
|
15313
|
+
}
|
|
15314
|
+
updateSiblingBlocks(sound) {
|
|
15315
|
+
this.setNumberInputValue(this.options.durationInputName, sound.duration);
|
|
15316
|
+
this.setNumberInputValue(this.options.startFrequencyInputName, sound.startFrequency);
|
|
15317
|
+
this.setNumberInputValue(this.options.endFrequencyInputName, sound.endFrequency);
|
|
15318
|
+
this.setNumberInputValue(this.options.startVolumeInputName, sound.startVolume);
|
|
15319
|
+
this.setNumberInputValue(this.options.endVolumeInputName, sound.endVolume);
|
|
15320
|
+
this.setFieldDropdownValue(this.options.waveFieldName, waveformMapping[sound.wave]);
|
|
15321
|
+
this.setFieldDropdownValue(this.options.interpolationFieldName, interpolationMapping[sound.interpolation]);
|
|
15322
|
+
this.setFieldDropdownValue(this.options.effectFieldName, effectMapping[sound.effect]);
|
|
15323
|
+
}
|
|
15324
|
+
setNumberInputValue(name, value) {
|
|
15325
|
+
const block = this.getSiblingBlock(name) || this.getSiblingBlock(name, true);
|
|
15326
|
+
if (!block)
|
|
15327
|
+
return;
|
|
15328
|
+
if (block.type === "math_number" || block.type === "math_integer" || block.type === "math_whole_number") {
|
|
15329
|
+
block.setFieldValue(Math.round(value), "NUM");
|
|
15330
|
+
}
|
|
15331
|
+
else if (block.type === "math_number_minmax") {
|
|
15332
|
+
block.setFieldValue(Math.round(value), "SLIDER");
|
|
15333
|
+
}
|
|
15334
|
+
}
|
|
15335
|
+
getNumberInputValue(name, defaultValue) {
|
|
15336
|
+
const block = this.getSiblingBlock(name) || this.getSiblingBlock(name, true);
|
|
15337
|
+
if (!block)
|
|
15338
|
+
return defaultValue;
|
|
15339
|
+
if (block.type === "math_number" || block.type === "math_integer" || block.type === "math_whole_number") {
|
|
15340
|
+
return parseInt(block.getFieldValue("NUM") + "");
|
|
15341
|
+
}
|
|
15342
|
+
else if (block.type === "math_number_minmax") {
|
|
15343
|
+
return parseInt(block.getFieldValue("SLIDER") + "");
|
|
15344
|
+
}
|
|
15345
|
+
return defaultValue;
|
|
15346
|
+
}
|
|
15347
|
+
fireNumberInputUpdate(name, oldValue) {
|
|
15348
|
+
const block = this.getSiblingBlock(name) || this.getSiblingBlock(name, true);
|
|
15349
|
+
if (!block)
|
|
15350
|
+
return;
|
|
15351
|
+
let fieldName;
|
|
15352
|
+
if (block.type === "math_number" || block.type === "math_integer" || block.type === "math_whole_number") {
|
|
15353
|
+
fieldName = "NUM";
|
|
15354
|
+
}
|
|
15355
|
+
else if (block.type === "math_number_minmax") {
|
|
15356
|
+
fieldName = "SLIDER";
|
|
15357
|
+
}
|
|
15358
|
+
if (!fieldName)
|
|
15359
|
+
return;
|
|
15360
|
+
Blockly.Events.fire(new Blockly.Events.Change(block, "field", fieldName, oldValue, this.getNumberInputValue(name, oldValue)));
|
|
15361
|
+
}
|
|
15362
|
+
setFieldDropdownValue(name, value) {
|
|
15363
|
+
const field = this.getSiblingField(name) || this.getSiblingField(name, true);
|
|
15364
|
+
if (!field)
|
|
15365
|
+
return;
|
|
15366
|
+
field.setValue(value);
|
|
15367
|
+
}
|
|
15368
|
+
getFieldDropdownValue(name) {
|
|
15369
|
+
const field = this.getSiblingField(name) || this.getSiblingField(name, true);
|
|
15370
|
+
if (!field)
|
|
15371
|
+
return undefined;
|
|
15372
|
+
return field.getValue();
|
|
15373
|
+
}
|
|
15374
|
+
fireFieldDropdownUpdate(name, oldValue) {
|
|
15375
|
+
const field = this.getSiblingField(name) || this.getSiblingField(name, true);
|
|
15376
|
+
if (!field)
|
|
15377
|
+
return;
|
|
15378
|
+
Blockly.Events.fire(new Blockly.Events.Change(field.sourceBlock_, "field", field.name, oldValue, this.getFieldDropdownValue(name)));
|
|
15379
|
+
}
|
|
15380
|
+
readCurrentSound() {
|
|
15381
|
+
const savedSound = this.readBlockDataSound();
|
|
15382
|
+
return {
|
|
15383
|
+
duration: this.getNumberInputValue(this.options.durationInputName, savedSound.duration),
|
|
15384
|
+
startFrequency: this.getNumberInputValue(this.options.startFrequencyInputName, savedSound.startFrequency),
|
|
15385
|
+
endFrequency: this.getNumberInputValue(this.options.endFrequencyInputName, savedSound.endFrequency),
|
|
15386
|
+
startVolume: this.getNumberInputValue(this.options.startVolumeInputName, savedSound.startVolume),
|
|
15387
|
+
endVolume: this.getNumberInputValue(this.options.endVolumeInputName, savedSound.endVolume),
|
|
15388
|
+
wave: reverseLookup(waveformMapping, this.getFieldDropdownValue(this.options.waveFieldName)) || savedSound.wave,
|
|
15389
|
+
interpolation: reverseLookup(interpolationMapping, this.getFieldDropdownValue(this.options.interpolationFieldName)) || savedSound.interpolation,
|
|
15390
|
+
effect: reverseLookup(effectMapping, this.getFieldDropdownValue(this.options.effectFieldName)) || savedSound.effect,
|
|
15391
|
+
};
|
|
15392
|
+
}
|
|
15393
|
+
// This stores the values of the fields in case a block (e.g. a variable) is placed in one
|
|
15394
|
+
// of the inputs.
|
|
15395
|
+
readBlockDataSound() {
|
|
15396
|
+
const data = this.getBlockData();
|
|
15397
|
+
let sound;
|
|
15398
|
+
try {
|
|
15399
|
+
sound = JSON.parse(data);
|
|
15400
|
+
}
|
|
15401
|
+
catch (e) {
|
|
15402
|
+
sound = {
|
|
15403
|
+
duration: 1000,
|
|
15404
|
+
startFrequency: 100,
|
|
15405
|
+
endFrequency: 1800,
|
|
15406
|
+
startVolume: 1023,
|
|
15407
|
+
endVolume: 0,
|
|
15408
|
+
wave: "sine",
|
|
15409
|
+
interpolation: "linear",
|
|
15410
|
+
effect: "none"
|
|
15411
|
+
};
|
|
15412
|
+
}
|
|
15413
|
+
return sound;
|
|
15414
|
+
}
|
|
15415
|
+
}
|
|
15416
|
+
pxtblockly.FieldSoundEffect = FieldSoundEffect;
|
|
15417
|
+
const waveformMapping = {
|
|
15418
|
+
"sine": "WaveShape.Sine",
|
|
15419
|
+
"square": "WaveShape.Square",
|
|
15420
|
+
"sawtooth": "WaveShape.Sawtooth",
|
|
15421
|
+
"triangle": "WaveShape.Triangle",
|
|
15422
|
+
"noise": "WaveShape.Noise",
|
|
15423
|
+
};
|
|
15424
|
+
const effectMapping = {
|
|
15425
|
+
"none": "SoundExpressionEffect.None",
|
|
15426
|
+
"vibrato": "SoundExpressionEffect.Vibrato",
|
|
15427
|
+
"tremolo": "SoundExpressionEffect.Tremolo",
|
|
15428
|
+
"warble": "SoundExpressionEffect.Warble",
|
|
15429
|
+
};
|
|
15430
|
+
const interpolationMapping = {
|
|
15431
|
+
"linear": "InterpolationCurve.Linear",
|
|
15432
|
+
"curve": "InterpolationCurve.Curve",
|
|
15433
|
+
"logarithmic": "InterpolationCurve.Logarithmic",
|
|
15434
|
+
};
|
|
15435
|
+
function reverseLookup(map, value) {
|
|
15436
|
+
return Object.keys(map).find(k => map[k] === value);
|
|
15437
|
+
}
|
|
15438
|
+
})(pxtblockly || (pxtblockly = {}));
|
|
15076
15439
|
/// <reference path="../../localtypings/blockly.d.ts"/>
|
|
15077
15440
|
/// <reference path="../../built/pxtsim.d.ts"/>
|
|
15078
15441
|
var pxtblockly;
|
|
@@ -16538,4 +16901,21 @@ var pxtblockly;
|
|
|
16538
16901
|
}
|
|
16539
16902
|
}
|
|
16540
16903
|
pxtblockly.getTemporaryAssets = getTemporaryAssets;
|
|
16904
|
+
function workspaceToScreenCoordinates(ws, wsCoordinates) {
|
|
16905
|
+
// The position in pixels relative to the origin of the
|
|
16906
|
+
// main workspace.
|
|
16907
|
+
const scaledWS = wsCoordinates.scale(ws.scale);
|
|
16908
|
+
// The offset in pixels between the main workspace's origin and the upper
|
|
16909
|
+
// left corner of the injection div.
|
|
16910
|
+
const mainOffsetPixels = ws.getOriginOffsetInPixels();
|
|
16911
|
+
// The client coordinates offset by the injection div's upper left corner.
|
|
16912
|
+
const clientOffsetPixels = Blockly.utils.Coordinate.sum(scaledWS, mainOffsetPixels);
|
|
16913
|
+
const injectionDiv = ws.getInjectionDiv();
|
|
16914
|
+
// Bounding rect coordinates are in client coordinates, meaning that they
|
|
16915
|
+
// are in pixels relative to the upper left corner of the visible browser
|
|
16916
|
+
// window. These coordinates change when you scroll the browser window.
|
|
16917
|
+
const boundingRect = injectionDiv.getBoundingClientRect();
|
|
16918
|
+
return new Blockly.utils.Coordinate(clientOffsetPixels.x + boundingRect.left, clientOffsetPixels.y + boundingRect.top);
|
|
16919
|
+
}
|
|
16920
|
+
pxtblockly.workspaceToScreenCoordinates = workspaceToScreenCoordinates;
|
|
16541
16921
|
})(pxtblockly || (pxtblockly = {}));
|