Skip to content

Commit

Permalink
Add a Blockly block to define key/value dictionaries (#1209)
Browse files Browse the repository at this point in the history
Signed-off-by: Yannick Schaus <github@schaus.net>
  • Loading branch information
ghys committed Nov 20, 2021
1 parent 6a3d035 commit bf99312
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import Blockly from 'blockly'

export default function (f7) {
Blockly.Blocks['dicts_create_with'] = {
/**
* Block for creating a list with any number of elements of any type.
* @this {Blockly.Block}
*/
init: function () {
this.setStyle('list_blocks')
this.itemCount_ = 3
this.updateShape_()
this.setOutput(true, 'Dictionary')
this.setMutator(new Blockly.Mutator(['dicts_create_with_item']))
this.setTooltip('Create a key/value dictionary')
},
/**
* Create XML to represent list inputs.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function () {
let container = Blockly.utils.xml.createElement('mutation')
container.setAttribute('items', this.itemCount_)
return container
},
/**
* Parse XML to restore the list inputs.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function (xmlElement) {
this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10)
this.updateShape_()
},
/**
* 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) {
let containerBlock = workspace.newBlock('dicts_create_with_container')
containerBlock.initSvg()
let connection = containerBlock.getInput('STACK').connection
for (let i = 0; i < this.itemCount_; i++) {
let itemBlock = workspace.newBlock('dicts_create_with_item')
itemBlock.initSvg()
connection.connect(itemBlock.previousConnection)
connection = itemBlock.nextConnection
}
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) {
let itemBlock = containerBlock.getInputTargetBlock('STACK')
// Count number of inputs.
let connections = []
while (itemBlock && !itemBlock.isInsertionMarker()) {
connections.push(itemBlock.valueConnection_)
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock()
}
// Disconnect any children that don't belong.
for (let i = 0; i < this.itemCount_; i++) {
let input = this.getInput('ADD' + i)
if (!input) continue
let connection = input.connection.targetConnection
if (connection && connections.indexOf(connection) === -1) {
connection.disconnect()
}
}
this.itemCount_ = connections.length
this.updateShape_()
// Reconnect any child blocks.
for (let i = 0; i < this.itemCount_; i++) {
Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i)
}
},
/**
* Store pointers to any connected child blocks.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this {Blockly.Block}
*/
saveConnections: function (containerBlock) {
let itemBlock = containerBlock.getInputTargetBlock('STACK')
let i = 0
while (itemBlock) {
let input = this.getInput('ADD' + i)
itemBlock.valueConnection_ = input && input.connection.targetConnection
i++
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock()
}
},
/**
* Modify this block to have the correct number of inputs.
* @private
* @this {Blockly.Block}
*/
updateShape_: function () {
if (this.itemCount_ && this.getInput('EMPTY')) {
this.removeInput('EMPTY')
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
this.appendDummyInput('EMPTY')
.appendField('create empty dictionary')
}
// Add new inputs.
let i
for (i = 0; i < this.itemCount_; i++) {
if (!this.getInput('ADD' + i)) {
let input = this.appendValueInput('ADD' + i)
.setAlign(Blockly.ALIGN_RIGHT)
if (i === 0) {
input.appendField('dictionary of')
}
input.appendField(new Blockly.FieldTextInput('key' + i), 'KEY' + i)
}
}
// Remove deleted inputs.
while (this.getInput('ADD' + i)) {
this.removeInput('ADD' + i)
i++
}
}
}

Blockly.Blocks['dicts_create_with_container'] = {
/**
* Mutator block for list container.
* @this {Blockly.Block}
*/
init: function () {
this.setStyle('list_blocks')
this.appendDummyInput()
.appendField('key/values')
this.appendStatementInput('STACK')
this.setTooltip('Initialize a Dictionary')
this.contextMenu = false
}
}

Blockly.Blocks['dicts_create_with_item'] = {
/**
* Mutator block for adding items.
* @this {Blockly.Block}
*/
init: function () {
this.setStyle('list_blocks')
this.appendDummyInput()
.appendField('key')
this.setPreviousStatement(true)
this.setNextStatement(true)
this.setTooltip('add a key/value to the dictionary')
this.contextMenu = false
}
}

Blockly.JavaScript['dicts_create_with'] = function (block) {
// Create an object with any number of elements of any type.
let elements = new Array(block.itemCount_)
for (let i = 0; i < block.itemCount_; i++) {
elements[i] = '\'' + block.getFieldValue('KEY' + i) + '\': '
elements[i] += Blockly.JavaScript.valueToCode(block, 'ADD' + i,
Blockly.JavaScript.ORDER_NONE) || 'null'
}
let code = '{' + elements.join(', ') + '}'
return [code, Blockly.JavaScript.ORDER_ATOMIC]
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import defineDictionaryBlocks from './blocks-dicts'
import defineItemBlocks from './blocks-items'
import defineThingsBlocks from './blocks-things'
import defineAudioBlocks from './blocks-audio'
Expand All @@ -8,6 +9,7 @@ import defineTimerBlocks from './blocks-timers'
import defineValueStorageBlocks from './blocks-valuestorage'

export default function (f7, data) {
defineDictionaryBlocks(f7)
defineItemBlocks(f7)
defineThingsBlocks(f7)
defineAudioBlocks(f7, data.sinks, data.voices)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@
</value>
</block>
<block type="lists_sort" />
<sep gap="48" />
<block type="dicts_create_with" />
</category>

<category name="Color" colour="%{BKY_COLOUR_HUE}">
Expand Down

0 comments on commit bf99312

Please sign in to comment.