Skip to content

Commit 205a18f

Browse files
authored
Various Expandable Clean Up
- make utility function public and accessible - move expandable out of livetests - fix shadows not saving their values when hidden - fix block spam unintentionally
1 parent 29a7306 commit 205a18f

4 files changed

Lines changed: 166 additions & 179 deletions

File tree

blocks_vertical/control.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,140 @@ Blockly.Blocks['control_if_else'] = {
215215
}
216216
};
217217

218+
Blockly.Blocks['control_expandableIf'] = {
219+
/**
220+
* pm: Block for expandable if else
221+
* @this Blockly.Block
222+
*/
223+
init: function () {
224+
this.jsonInit({
225+
"message0": 'hidden %1 %2',
226+
"args0": [
227+
{
228+
"type": "field_expandable_remove",
229+
"name": "REMOVE"
230+
},
231+
{
232+
"type": "field_expandable_add",
233+
"name": "ADD"
234+
}
235+
],
236+
"category": Blockly.Categories.control,
237+
"extensions": ["colours_control", "shape_statement"]
238+
});
239+
240+
this.branches_ = 1;
241+
if (this.isInFlyout) this.addCase(true);
242+
this.nextIsElse = true;
243+
this.endsInElse = false;
244+
},
245+
246+
fillInBlock: Blockly.scratchBlocksUtils.generateMutatorShadow,
247+
fixupButtons: function () {
248+
const expandableInput = this.getInput("");
249+
this.inputList.splice(this.inputList.indexOf(expandableInput), 1);
250+
this.inputList.push(expandableInput);
251+
252+
expandableInput.setAlign(1);
253+
const hiddenBtn = expandableInput.fieldRow[0];
254+
hiddenBtn.size_.width = 0.5;
255+
hiddenBtn.size_.height = Blockly.BlockSvg.INPUT_SHAPE_HEIGHT + 16;
256+
hiddenBtn.setVisible(false);
257+
},
258+
addCase: function (shouldPopulate) {
259+
if (this.nextIsElse) {
260+
this.appendDummyInput(`TEXTSTART${this.branches_}`).appendField("else");
261+
this.appendStatementInput(`SUBSTACK${this.branches_}`).setCheck("normal");
262+
this.endsInElse = true;
263+
} else {
264+
const prevText = this.getInput(`TEXTSTART${this.branches_}`);
265+
if (prevText) prevText.appendField("if");
266+
else this.appendDummyInput(`TEXTSTART${this.branches_}`).appendField("if");
267+
const input = this.appendValueInput(`BOOL${this.branches_}`);
268+
if (shouldPopulate) this.fillInBlock(input.connection, "checkbox");
269+
this.appendDummyInput(`TEXTEND${this.branches_}`).appendField("then");
270+
271+
// swap out the connection with the old and new branch
272+
const prevBranch = this.getInput(`SUBSTACK${this.branches_}`);
273+
const newBranch = this.appendStatementInput(`SUBSTACK${this.branches_}`).setCheck("normal");
274+
if (this.branches_ > 1) {
275+
const prevBranchBlock = prevBranch.connection.targetBlock();
276+
if (prevBranchBlock) newBranch.connection.connect(prevBranchBlock.previousConnection);
277+
this.removeInput(`SUBSTACK${this.branches_}`);
278+
}
279+
this.endsInElse = false;
280+
}
281+
282+
this.fixupButtons();
283+
},
284+
285+
mutationToDom: function () {
286+
// on save
287+
const container = document.createElement("mutation");
288+
container.setAttribute("branches", String(this.branches_));
289+
container.setAttribute("ends-in-else", String(this.endsInElse));
290+
return container;
291+
},
292+
293+
domToMutation: function (xmlElement) {
294+
// on load
295+
const inputCount = Number(xmlElement.getAttribute("branches"));
296+
let branchCount = isNaN(inputCount) ? 0 : inputCount;
297+
if (branchCount > 1) {
298+
branchCount = (branchCount * 2) - 1;
299+
if (xmlElement.getAttribute("ends-in-else") === "true") branchCount -= 1;
300+
}
301+
302+
this.nextIsElse = false;
303+
this.endsInElse = false;
304+
this.branches_ = 1;
305+
for (let i = 0; i < branchCount; i++) {
306+
if (this.nextIsElse) this.branches_++;
307+
// vm handles shadow values
308+
this.addCase(false);
309+
this.nextIsElse = !this.nextIsElse;
310+
}
311+
312+
this.fixupButtons();
313+
},
314+
315+
onExpandableButtonClicked_: function (isAdding) {
316+
// Create an event group to keep field value and mutator in sync
317+
// Return null at the end because setValue is called here already.
318+
if (this.isInFlyout) return;
319+
Blockly.Events.setGroup(true);
320+
var oldMutation = Blockly.Xml.domToText(this.mutationToDom());
321+
if (isAdding) {
322+
if (this.nextIsElse) this.branches_++;
323+
this.addCase(true);
324+
this.nextIsElse = !this.nextIsElse;
325+
} else if (this.branches_ > 1) {
326+
const boolInput = this.getInput(`BOOL${this.branches_}`);
327+
if (boolInput) {
328+
const block = boolInput.connection.targetBlock();
329+
if (block.type === "checkbox") block.dispose();
330+
else block.outputConnection.disconnect();
331+
}
332+
333+
this.removeInput(`BOOL${this.branches_}`);
334+
this.removeInput(`SUBSTACK${this.branches_}`);
335+
this.removeInput(`TEXTSTART${this.branches_}`);
336+
this.removeInput(`TEXTEND${this.branches_}`);
337+
this.branches_--;
338+
this.nextIsElse = true;
339+
this.endsInElse = false;
340+
}
341+
342+
this.initSvg();
343+
if (this.rendered) this.render();
344+
345+
var newMutation = Blockly.Xml.domToText(this.mutationToDom());
346+
Blockly.Events.fire(new Blockly.Events.BlockChange(
347+
this, 'mutation', null, oldMutation, newMutation
348+
));
349+
Blockly.Events.setGroup(false);
350+
}
351+
};
218352

219353
Blockly.Blocks['control_try_catch'] = {
220354
/**

blocks_vertical/live_tests.js

Lines changed: 0 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -6,161 +6,6 @@ goog.require('Blockly.Blocks');
66
goog.require('Blockly.Colours');
77
goog.require('Blockly.ScratchBlocks.VerticalExtensions');
88

9-
const mutatorPopulateUtil2 = function (connection, type, optValue, optValueName) {
10-
if (connection.sourceBlock_.isInsertionMarker_) return;
11-
12-
ScratchBlocks.Events.disable();
13-
const block = this.workspace.newBlock(type);
14-
try {
15-
if (optValue) block.setFieldValue(optValue, optValueName);
16-
block.setShadow(true);
17-
if (!this.isInsertionMarker()) {
18-
block.initSvg();
19-
block.render(false);
20-
}
21-
} finally {
22-
ScratchBlocks.Events.enable();
23-
}
24-
25-
if (ScratchBlocks.Events.isEnabled()) ScratchBlocks.Events.fire(new ScratchBlocks.Events.BlockCreate(block));
26-
if (block.outputConnection) block.outputConnection.connect(connection);
27-
else block.previousConnection.connect(connection);
28-
}
29-
30-
Blockly.Blocks['control_expandableIf'] = {
31-
/**
32-
* pm: add this description before publishing...
33-
* @this Blockly.Block
34-
*/
35-
init: function () {
36-
this.jsonInit({
37-
"message0": 'hidden %1 %2',
38-
"args0": [
39-
{
40-
"type": "field_expandable_remove",
41-
"name": "REMOVE"
42-
},
43-
{
44-
"type": "field_expandable_add",
45-
"name": "ADD"
46-
}
47-
],
48-
"category": Blockly.Categories.control,
49-
"extensions": ["colours_control", "shape_statement"]
50-
});
51-
52-
this.branches_ = 1;
53-
if (this.isInFlyout) this.addCase();
54-
this.nextIsElse = true;
55-
this.endsInElse = false;
56-
},
57-
58-
fillInBlock: mutatorPopulateUtil2,
59-
fixupButtons: function () {
60-
const expandableInput = this.getInput("");
61-
this.inputList.splice(this.inputList.indexOf(expandableInput), 1);
62-
this.inputList.push(expandableInput);
63-
64-
expandableInput.setAlign(1);
65-
const hiddenBtn = expandableInput.fieldRow[0];
66-
hiddenBtn.size_.width = 0.5;
67-
hiddenBtn.size_.height = Blockly.BlockSvg.INPUT_SHAPE_HEIGHT + 16;
68-
hiddenBtn.setVisible(false);
69-
},
70-
addCase: function () {
71-
if (this.nextIsElse) {
72-
this.appendDummyInput(`TEXTSTART${this.branches_}`).appendField("else");
73-
this.appendStatementInput(`SUBSTACK${this.branches_}`);
74-
this.endsInElse = true;
75-
} else {
76-
const prevText = this.getInput(`TEXTSTART${this.branches_}`);
77-
if (prevText) prevText.appendField("if");
78-
else this.appendDummyInput(`TEXTSTART${this.branches_}`).appendField("if");
79-
const input = this.appendValueInput(`BOOL${this.branches_}`);
80-
this.fillInBlock(input.connection, "checkbox");
81-
this.appendDummyInput(`TEXTEND${this.branches_}`).appendField("then");
82-
83-
// swap out the connection with the old and new branch
84-
const prevBranch = this.getInput(`SUBSTACK${this.branches_}`);
85-
const newBranch = this.appendStatementInput(`SUBSTACK${this.branches_}`);
86-
if (this.branches_ > 1) {
87-
const prevBranchBlock = prevBranch.connection.targetBlock();
88-
if (prevBranchBlock) newBranch.connection.connect(prevBranchBlock.previousConnection);
89-
this.removeInput(`SUBSTACK${this.branches_}`);
90-
}
91-
this.endsInElse = false;
92-
}
93-
94-
this.fixupButtons();
95-
},
96-
97-
mutationToDom: function () {
98-
// on save
99-
const container = document.createElement("mutation");
100-
container.setAttribute("branches", String(this.branches_));
101-
container.setAttribute("ends-in-else", String(this.endsInElse));
102-
return container;
103-
},
104-
105-
domToMutation: function (xmlElement) {
106-
// on load
107-
const inputCount = Number(xmlElement.getAttribute("branches"));
108-
let branchCount = isNaN(inputCount) ? 0 : inputCount;
109-
if (branchCount > 1) {
110-
branchCount = (branchCount * 2) - 1;
111-
if (xmlElement.getAttribute("ends-in-else") === "true") branchCount -= 1;
112-
}
113-
114-
this.nextIsElse = false;
115-
this.endsInElse = false;
116-
this.branches_ = 1;
117-
for (let i = 0; i < branchCount; i++) {
118-
if (this.nextIsElse) this.branches_++;
119-
this.addCase();
120-
this.nextIsElse = !this.nextIsElse;
121-
}
122-
123-
this.fixupButtons();
124-
},
125-
126-
onExpandableButtonClicked_: function (isAdding) {
127-
// Create an event group to keep field value and mutator in sync
128-
// Return null at the end because setValue is called here already.
129-
if (this.isInFlyout) return;
130-
Blockly.Events.setGroup(true);
131-
var oldMutation = Blockly.Xml.domToText(this.mutationToDom());
132-
if (isAdding) {
133-
if (this.nextIsElse) this.branches_++;
134-
this.addCase();
135-
this.nextIsElse = !this.nextIsElse;
136-
} else if (this.branches_ > 1) {
137-
const boolInput = this.getInput(`BOOL${this.branches_}`);
138-
if (boolInput) {
139-
const block = boolInput.connection.targetBlock();
140-
if (block.type === "checkbox") block.dispose();
141-
else block.outputConnection.disconnect();
142-
}
143-
144-
this.removeInput(`BOOL${this.branches_}`);
145-
this.removeInput(`SUBSTACK${this.branches_}`);
146-
this.removeInput(`TEXTSTART${this.branches_}`);
147-
this.removeInput(`TEXTEND${this.branches_}`);
148-
this.branches_--;
149-
this.nextIsElse = true;
150-
this.endsInElse = false;
151-
}
152-
153-
this.initSvg();
154-
if (this.rendered) this.render();
155-
156-
var newMutation = Blockly.Xml.domToText(this.mutationToDom());
157-
Blockly.Events.fire(new Blockly.Events.BlockChange(
158-
this, 'mutation', null, oldMutation, newMutation
159-
));
160-
Blockly.Events.setGroup(false);
161-
}
162-
};
163-
1649
Blockly.Blocks['looks_setVertTransform'] = {
16510
/**
16611
* Block to report properties of sprites.

blocks_vertical/operators.js

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,6 @@ goog.require('Blockly.Colours');
2727
goog.require('Blockly.constants');
2828
goog.require('Blockly.ScratchBlocks.VerticalExtensions');
2929

30-
/* Utility function for Expandable Blocks */
31-
const mutatorPopulateUtil = function (connection, type, optValue, optValueName) {
32-
if (connection.sourceBlock_.isInsertionMarker_) return;
33-
34-
ScratchBlocks.Events.disable();
35-
const block = this.workspace.newBlock(type);
36-
try {
37-
if (optValue) block.setFieldValue(optValue, optValueName);
38-
block.setShadow(true);
39-
if (!this.isInsertionMarker()) {
40-
block.initSvg();
41-
block.render(false);
42-
}
43-
} finally {
44-
ScratchBlocks.Events.enable();
45-
}
46-
47-
if (ScratchBlocks.Events.isEnabled()) ScratchBlocks.Events.fire(new ScratchBlocks.Events.BlockCreate(block));
48-
if (block.outputConnection) block.outputConnection.connect(connection);
49-
else block.previousConnection.connect(connection);
50-
}
51-
5230
Blockly.Blocks['operator_add'] = {
5331
/**
5432
* Block for adding two numbers.
@@ -178,7 +156,7 @@ Blockly.Blocks['operator_expandableMath'] = {
178156
}
179157
},
180158

181-
fillInBlock: mutatorPopulateUtil,
159+
fillInBlock: Blockly.scratchBlocksUtils.generateMutatorShadow,
182160
menuGenerator: function() {
183161
const dropdown = new Blockly.FieldDropdown(function () {
184162
return [
@@ -688,7 +666,7 @@ Blockly.Blocks['operator_expandablejoininputs'] = {
688666
})
689667
},
690668

691-
fillInBlock: mutatorPopulateUtil,
669+
fillInBlock: Blockly.scratchBlocksUtils.generateMutatorShadow,
692670

693671
mutationToDom: function () {
694672
// on save

core/scratch_blocks_utils.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,33 @@ Blockly.scratchBlocksUtils.duplicateAndDragCallback = function(oldBlock, event)
237237
}, 0);
238238
};
239239
};
240+
241+
/**
242+
* Creates a shadow block of a inputted type and connects it to the source block.
243+
* Primarly used by mutations.
244+
* @param {!Blockly.Connection} connection The connection input from the source block that is to be connected
245+
* @param {!String} type Field/Opcode that represents the shadow (text, math_number, etc)
246+
* @param {!String} optValue Optional param that sets the shadows value
247+
* @param {!String} optValueName Required if using 'optValue', represents the name of the shadows field
248+
* @package
249+
*/
250+
Blockly.scratchBlocksUtils.generateMutatorShadow = function (connection, type, optValue, optValueName) {
251+
if (connection.sourceBlock_.isInsertionMarker_) return;
252+
253+
Blockly.Events.disable();
254+
const block = this.workspace.newBlock(type);
255+
try {
256+
if (optValue) block.setFieldValue(optValue, optValueName);
257+
block.setShadow(true);
258+
if (!this.isInsertionMarker()) {
259+
block.initSvg();
260+
block.render(false);
261+
}
262+
} finally {
263+
Blockly.Events.enable();
264+
}
265+
266+
if (Blockly.Events.isEnabled()) Blockly.Events.fire(new Blockly.Events.BlockCreate(block));
267+
if (block.outputConnection) block.outputConnection.connect(connection);
268+
else block.previousConnection.connect(connection);
269+
};

0 commit comments

Comments
 (0)