diff --git a/src/blocks/mrc_call_python_function.ts b/src/blocks/mrc_call_python_function.ts index 4000feba..9944a601 100644 --- a/src/blocks/mrc_call_python_function.ts +++ b/src/blocks/mrc_call_python_function.ts @@ -66,281 +66,6 @@ export type FunctionArg = { const WARNING_ID_FUNCTION_CHANGED = 'function changed'; -// Functions used for creating blocks for the toolbox. - -export function addBuiltInFunctionBlocks( - functions: FunctionData[], - contents: ToolboxItems.ContentsType[]) { - functions.forEach(functionData => { - contents.push(createBuiltInMethodBlock(functionData)); - }); -} - -function createBuiltInMethodBlock( - functionData: FunctionData): ToolboxItems.Block { - const extraState: CallPythonFunctionExtraState = { - functionKind: FunctionKind.BUILT_IN, - returnType: functionData.returnType, - args: [], - tooltip: functionData.tooltip, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_FUNCTION_NAME] = functionData.functionName; - const inputs: {[key: string]: any} = {}; - processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); - return createBlock(extraState, fields, inputs); -} - -function processArgs( - args: ArgData[], - extraState: CallPythonFunctionExtraState, - inputs: {[key: string]: any}, - declaringClassName?: string) { - for (let i = 0; i < args.length; i++) { - let argName = args[i].name; - if (i === 0 && argName === 'self' && declaringClassName) { - argName = Variable.getSelfArgName(declaringClassName); - } - extraState.args.push({ - 'name': argName, - 'type': args[i].type, - }); - // Check if we should plug a variable getter block into the argument input socket. - const input = Value.valueForFunctionArgInput(args[i].type, args[i].defaultValue); - if (input) { - inputs['ARG' + i] = input; - } - } -} - -function createBlock( - extraState: CallPythonFunctionExtraState, - fields: {[key: string]: any}, - inputs: {[key: string]: any}): ToolboxItems.Block { - let block = new ToolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); - if (extraState.returnType && extraState.returnType != 'None') { - const varName = Variable.varNameForType(extraState.returnType); - if (varName) { - block = Variable.createVariableSetterBlock(varName, block); - } - } - return block; -} - -export function addModuleFunctionBlocks( - moduleName: string, - functions: FunctionData[], - contents: ToolboxItems.ContentsType[]) { - functions.forEach(functionData => { - const block = createModuleFunctionOrStaticMethodBlock( - FunctionKind.MODULE, moduleName, moduleName, functionData); - contents.push(block); - }); -} - -export function addStaticMethodBlocks( - importModule: string, - functions: FunctionData[], - contents: ToolboxItems.ContentsType[]) { - functions.forEach(functionData => { - if (functionData.declaringClassName) { - const block = createModuleFunctionOrStaticMethodBlock( - FunctionKind.STATIC, importModule, functionData.declaringClassName, functionData); - contents.push(block); - } - }); -} - -function createModuleFunctionOrStaticMethodBlock( - functionKind: FunctionKind, - importModule: string, - moduleOrClassName: string, - functionData: FunctionData): ToolboxItems.Block { - const extraState: CallPythonFunctionExtraState = { - functionKind: functionKind, - returnType: functionData.returnType, - args: [], - tooltip: functionData.tooltip, - importModule: importModule, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_MODULE_OR_CLASS_NAME] = moduleOrClassName; - fields[FIELD_FUNCTION_NAME] = functionData.functionName; - const inputs: {[key: string]: any} = {}; - processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); - return createBlock(extraState, fields, inputs); -} - -export function addConstructorBlocks( - importModule: string, - functions: FunctionData[], - contents: ToolboxItems.ContentsType[]) { - functions.forEach(functionData => { - contents.push(createConstructorBlock(importModule, functionData)); - }); -} - -function createConstructorBlock( - importModule: string, - functionData: FunctionData): ToolboxItems.Block { - const extraState: CallPythonFunctionExtraState = { - functionKind: FunctionKind.CONSTRUCTOR, - returnType: functionData.returnType, - args: [], - tooltip: functionData.tooltip, - importModule: importModule, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_MODULE_OR_CLASS_NAME] = functionData.declaringClassName; - const inputs: {[key: string]: any} = {}; - processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); - return createBlock(extraState, fields, inputs); -} - -export function addInstanceMethodBlocks( - functions: FunctionData[], - contents: ToolboxItems.ContentsType[]) { - functions.forEach(functionData => { - contents.push(createInstanceMethodBlock(functionData)); - }); -} - -function createInstanceMethodBlock( - functionData: FunctionData): ToolboxItems.Block { - const extraState: CallPythonFunctionExtraState = { - functionKind: FunctionKind.INSTANCE, - returnType: functionData.returnType, - args: [], - tooltip: functionData.tooltip, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_MODULE_OR_CLASS_NAME] = functionData.declaringClassName; - fields[FIELD_FUNCTION_NAME] = functionData.functionName; - const inputs: {[key: string]: any} = {}; - processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); - return createBlock(extraState, fields, inputs); -} - -export function addInstanceWithinBlocks( - methods: CommonStorage.Method[], - contents: ToolboxItems.ContentsType[]) { - methods.forEach(method => { - contents.push(createInstanceWithinBlock(method)); - }); -} - -function createInstanceWithinBlock(method: CommonStorage.Method): ToolboxItems.Block { - const extraState: CallPythonFunctionExtraState = { - functionKind: FunctionKind.INSTANCE_WITHIN, - returnType: method.returnType, - actualFunctionName: method.pythonName, - args: [], - classMethodDefBlockId: method.blockId, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_FUNCTION_NAME] = method.visibleName; - const inputs: {[key: string]: any} = {}; - // Convert method.args from CommonStorage.MethodArg[] to ArgData[]. - const args: ArgData[] = []; - // We don't include the arg for the self argument. - for (let i = 1; i < method.args.length; i++) { - args.push({ - name: method.args[i].name, - type: method.args[i].type, - defaultValue: '', - }); - } - processArgs(args, extraState, inputs); - return createBlock(extraState, fields, inputs); -} - -export function getInstanceComponentBlocks( - component: CommonStorage.Component): ToolboxItems.ContentsType[] { - const contents: ToolboxItems.ContentsType[] = []; - - const classData = getClassData(component.className); - if (!classData) { - throw new Error('Could not find classData for ' + component.className); - } - const functions = classData.instanceMethods; - - const componentClassData = getClassData('component.Component'); - if (!componentClassData) { - throw new Error('Could not find classData for component.Component'); - } - const componentFunctions = componentClassData.instanceMethods; - - for (const functionData of functions) { - // Skip the functions that are also defined in componentFunctions. - if (findSuperFunctionData(functionData, componentFunctions)) { - continue; - } - const block = createInstanceComponentBlock(component, functionData); - contents.push(block); - } - - return contents; -} - -function createInstanceComponentBlock( - component: CommonStorage.Component, functionData: FunctionData): ToolboxItems.Block { - const extraState: CallPythonFunctionExtraState = { - functionKind: FunctionKind.INSTANCE_COMPONENT, - returnType: functionData.returnType, - args: [], - tooltip: functionData.tooltip, - importModule: '', - componentClassName: component.className, - componentName: component.name, - componentBlockId: component.blockId, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_COMPONENT_NAME] = component.name; - fields[FIELD_FUNCTION_NAME] = functionData.functionName; - const inputs: {[key: string]: any} = {}; - // For INSTANCE_COMPONENT functions, the 0 argument is 'self', but - // self is represented by the FIELD_COMPONENT_NAME field. - // We don't include the arg for self. - const argsWithoutSelf = functionData.args.slice(1); - processArgs(argsWithoutSelf, extraState, inputs); - return createBlock(extraState, fields, inputs); -} - -export function addInstanceRobotBlocks( - methods: CommonStorage.Method[], - contents: ToolboxItems.ContentsType[]) { - methods.forEach(method => { - contents.push(createInstanceRobotBlock(method)); - }); -} - -function createInstanceRobotBlock(method: CommonStorage.Method): ToolboxItems.Block { - const extraState: CallPythonFunctionExtraState = { - functionKind: FunctionKind.INSTANCE_ROBOT, - returnType: method.returnType, - actualFunctionName: method.pythonName, - args: [], - classMethodDefBlockId: method.blockId, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_FUNCTION_NAME] = method.visibleName; - const inputs: {[key: string]: any} = {}; - // Convert method.args from CommonStorage.MethodArg[] to ArgData[]. - const args: ArgData[] = []; - // We don't include the arg for the self argument. - for (let i = 1; i < method.args.length; i++) { - args.push({ - name: method.args[i].name, - type: method.args[i].type, - defaultValue: '', - }); - } - processArgs(args, extraState, inputs); - return createBlock(extraState, fields, inputs); -} - -//.............................................................................. - export type CallPythonFunctionBlock = Blockly.Block & CallPythonFunctionMixin & Blockly.BlockSvg; interface CallPythonFunctionMixin extends CallPythonFunctionMixinType { mrcFunctionKind: FunctionKind, @@ -910,78 +635,351 @@ export const pythonFromBlock = function( code = 'self.robot.' + functionName; break; } - default: - throw new Error('mrcFunctionKind has unexpected value: ' + callPythonFunctionBlock.mrcFunctionKind) - } - code += '(' + generateCodeForArguments(callPythonFunctionBlock, generator, argStartIndex) + ')'; - if (block.outputConnection) { - return [code, Order.FUNCTION_CALL]; - } else { - return code + '\n'; + default: + throw new Error('mrcFunctionKind has unexpected value: ' + callPythonFunctionBlock.mrcFunctionKind) + } + code += '(' + generateCodeForArguments(callPythonFunctionBlock, generator, argStartIndex) + ')'; + if (block.outputConnection) { + return [code, Order.FUNCTION_CALL]; + } else { + return code + '\n'; + } +}; + +function generateCodeForArguments( + block: CallPythonFunctionBlock, + generator: ExtendedPythonGenerator, + startIndex: number) { + let code = ''; + if (block.mrcArgs.length - startIndex === 1) { + code += generator.valueToCode(block, 'ARG' + startIndex, Order.NONE) || 'None'; + } else { + let delimiter = '\n' + generator.INDENT + generator.INDENT; + for (let i = startIndex; i < block.mrcArgs.length; i++) { + code += delimiter; + code += generator.valueToCode(block, 'ARG' + i, Order.NONE) || 'None'; + delimiter = ',\n' + generator.INDENT + generator.INDENT; + } + } + return code; +} + +function getMethodCallers(workspace: Blockly.Workspace, name: string): Blockly.Block[] { + return workspace.getBlocksByType('mrc_call_python_function').filter((block) => { + const callBlock = block as CallPythonFunctionBlock; + return ( + callBlock.mrcFunctionKind === FunctionKind.INSTANCE_WITHIN && + callBlock.getFieldValue(FIELD_FUNCTION_NAME) === name + ); + }); +} + +export function renameMethodCallers(workspace: Blockly.Workspace, oldName: string, newName: string): void { + for (const block of getMethodCallers(workspace, oldName)) { + (block as CallPythonFunctionBlock).renameMethod(newName); + } +} + +export function mutateMethodCallers( + workspace: Blockly.Workspace, methodName: string, defBlockExtraState: ClassMethodDefExtraState) { + const oldRecordUndo = Blockly.Events.getRecordUndo(); + + for (const block of getMethodCallers(workspace, methodName)) { + const callBlock = block as CallPythonFunctionBlock; + // Get the extra state before changing the call block. + const oldExtraState = callBlock.saveExtraState(); + + // Apply the changes. + callBlock.mutateMethod(defBlockExtraState); + + // Get the extra state after changing the call block. + const newExtraState = callBlock.saveExtraState(); + if (oldExtraState !== newExtraState) { + // Fire a change event, but don't record it as an undoable action. + Blockly.Events.setRecordUndo(false); + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))( + callBlock, + 'mutation', + null, + oldExtraState, + newExtraState, + ), + ); + Blockly.Events.setRecordUndo(oldRecordUndo); + } + } +} + +// Functions used for creating blocks for the toolbox. + +export function addBuiltInFunctionBlocks( + functions: FunctionData[], + contents: ToolboxItems.ContentsType[]) { + functions.forEach(functionData => { + contents.push(createBuiltInMethodBlock(functionData)); + }); +} + +function createBuiltInMethodBlock( + functionData: FunctionData): ToolboxItems.Block { + const extraState: CallPythonFunctionExtraState = { + functionKind: FunctionKind.BUILT_IN, + returnType: functionData.returnType, + args: [], + tooltip: functionData.tooltip, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_FUNCTION_NAME] = functionData.functionName; + const inputs: {[key: string]: any} = {}; + processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); + return createBlock(extraState, fields, inputs); +} + +function processArgs( + args: ArgData[], + extraState: CallPythonFunctionExtraState, + inputs: {[key: string]: any}, + declaringClassName?: string) { + for (let i = 0; i < args.length; i++) { + let argName = args[i].name; + if (i === 0 && argName === 'self' && declaringClassName) { + argName = Variable.getSelfArgName(declaringClassName); + } + extraState.args.push({ + 'name': argName, + 'type': args[i].type, + }); + // Check if we should plug a variable getter block into the argument input socket. + const input = Value.valueForFunctionArgInput(args[i].type, args[i].defaultValue); + if (input) { + inputs['ARG' + i] = input; + } + } +} + +function createBlock( + extraState: CallPythonFunctionExtraState, + fields: {[key: string]: any}, + inputs: {[key: string]: any}): ToolboxItems.Block { + let block = new ToolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); + if (extraState.returnType && extraState.returnType != 'None') { + const varName = Variable.varNameForType(extraState.returnType); + if (varName) { + block = Variable.createVariableSetterBlock(varName, block); + } } -}; + return block; +} -function generateCodeForArguments( - block: CallPythonFunctionBlock, - generator: ExtendedPythonGenerator, - startIndex: number) { - let code = ''; - if (block.mrcArgs.length - startIndex === 1) { - code += generator.valueToCode(block, 'ARG' + startIndex, Order.NONE) || 'None'; - } else { - let delimiter = '\n' + generator.INDENT + generator.INDENT; - for (let i = startIndex; i < block.mrcArgs.length; i++) { - code += delimiter; - code += generator.valueToCode(block, 'ARG' + i, Order.NONE) || 'None'; - delimiter = ',\n' + generator.INDENT + generator.INDENT; +export function addModuleFunctionBlocks( + moduleName: string, + functions: FunctionData[], + contents: ToolboxItems.ContentsType[]) { + functions.forEach(functionData => { + const block = createModuleFunctionOrStaticMethodBlock( + FunctionKind.MODULE, moduleName, moduleName, functionData); + contents.push(block); + }); +} + +export function addStaticMethodBlocks( + importModule: string, + functions: FunctionData[], + contents: ToolboxItems.ContentsType[]) { + functions.forEach(functionData => { + if (functionData.declaringClassName) { + const block = createModuleFunctionOrStaticMethodBlock( + FunctionKind.STATIC, importModule, functionData.declaringClassName, functionData); + contents.push(block); } - } - return code; + }); } -function getMethodCallers(workspace: Blockly.Workspace, name: string): Blockly.Block[] { - return workspace.getBlocksByType('mrc_call_python_function').filter((block) => { - const callBlock = block as CallPythonFunctionBlock; - return ( - callBlock.mrcFunctionKind === FunctionKind.INSTANCE_WITHIN && - callBlock.getFieldValue(FIELD_FUNCTION_NAME) === name - ); +function createModuleFunctionOrStaticMethodBlock( + functionKind: FunctionKind, + importModule: string, + moduleOrClassName: string, + functionData: FunctionData): ToolboxItems.Block { + const extraState: CallPythonFunctionExtraState = { + functionKind: functionKind, + returnType: functionData.returnType, + args: [], + tooltip: functionData.tooltip, + importModule: importModule, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_MODULE_OR_CLASS_NAME] = moduleOrClassName; + fields[FIELD_FUNCTION_NAME] = functionData.functionName; + const inputs: {[key: string]: any} = {}; + processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); + return createBlock(extraState, fields, inputs); +} + +export function addConstructorBlocks( + importModule: string, + functions: FunctionData[], + contents: ToolboxItems.ContentsType[]) { + functions.forEach(functionData => { + contents.push(createConstructorBlock(importModule, functionData)); }); } -export function renameMethodCallers(workspace: Blockly.Workspace, oldName: string, newName: string): void { - for (const block of getMethodCallers(workspace, oldName)) { - (block as CallPythonFunctionBlock).renameMethod(newName); +function createConstructorBlock( + importModule: string, + functionData: FunctionData): ToolboxItems.Block { + const extraState: CallPythonFunctionExtraState = { + functionKind: FunctionKind.CONSTRUCTOR, + returnType: functionData.returnType, + args: [], + tooltip: functionData.tooltip, + importModule: importModule, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_MODULE_OR_CLASS_NAME] = functionData.declaringClassName; + const inputs: {[key: string]: any} = {}; + processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); + return createBlock(extraState, fields, inputs); +} + +export function addInstanceMethodBlocks( + functions: FunctionData[], + contents: ToolboxItems.ContentsType[]) { + functions.forEach(functionData => { + contents.push(createInstanceMethodBlock(functionData)); + }); +} + +function createInstanceMethodBlock( + functionData: FunctionData): ToolboxItems.Block { + const extraState: CallPythonFunctionExtraState = { + functionKind: FunctionKind.INSTANCE, + returnType: functionData.returnType, + args: [], + tooltip: functionData.tooltip, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_MODULE_OR_CLASS_NAME] = functionData.declaringClassName; + fields[FIELD_FUNCTION_NAME] = functionData.functionName; + const inputs: {[key: string]: any} = {}; + processArgs(functionData.args, extraState, inputs, functionData.declaringClassName); + return createBlock(extraState, fields, inputs); +} + +export function addInstanceWithinBlocks( + methods: CommonStorage.Method[], + contents: ToolboxItems.ContentsType[]) { + methods.forEach(method => { + contents.push(createInstanceWithinBlock(method)); + }); +} + +function createInstanceWithinBlock(method: CommonStorage.Method): ToolboxItems.Block { + const extraState: CallPythonFunctionExtraState = { + functionKind: FunctionKind.INSTANCE_WITHIN, + returnType: method.returnType, + actualFunctionName: method.pythonName, + args: [], + classMethodDefBlockId: method.blockId, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_FUNCTION_NAME] = method.visibleName; + const inputs: {[key: string]: any} = {}; + // Convert method.args from CommonStorage.MethodArg[] to ArgData[]. + const args: ArgData[] = []; + // We don't include the arg for the self argument. + for (let i = 1; i < method.args.length; i++) { + args.push({ + name: method.args[i].name, + type: method.args[i].type, + defaultValue: '', + }); } + processArgs(args, extraState, inputs); + return createBlock(extraState, fields, inputs); } -export function mutateMethodCallers( - workspace: Blockly.Workspace, methodName: string, defBlockExtraState: ClassMethodDefExtraState) { - const oldRecordUndo = Blockly.Events.getRecordUndo(); +export function getInstanceComponentBlocks( + component: CommonStorage.Component): ToolboxItems.ContentsType[] { + const contents: ToolboxItems.ContentsType[] = []; - for (const block of getMethodCallers(workspace, methodName)) { - const callBlock = block as CallPythonFunctionBlock; - // Get the extra state before changing the call block. - const oldExtraState = callBlock.saveExtraState(); + const classData = getClassData(component.className); + if (!classData) { + throw new Error('Could not find classData for ' + component.className); + } + const functions = classData.instanceMethods; - // Apply the changes. - callBlock.mutateMethod(defBlockExtraState); + const componentClassData = getClassData('component.Component'); + if (!componentClassData) { + throw new Error('Could not find classData for component.Component'); + } + const componentFunctions = componentClassData.instanceMethods; - // Get the extra state after changing the call block. - const newExtraState = callBlock.saveExtraState(); - if (oldExtraState !== newExtraState) { - // Fire a change event, but don't record it as an undoable action. - Blockly.Events.setRecordUndo(false); - Blockly.Events.fire( - new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))( - callBlock, - 'mutation', - null, - oldExtraState, - newExtraState, - ), - ); - Blockly.Events.setRecordUndo(oldRecordUndo); + for (const functionData of functions) { + // Skip the functions that are also defined in componentFunctions. + if (findSuperFunctionData(functionData, componentFunctions)) { + continue; } + const block = createInstanceComponentBlock(component, functionData); + contents.push(block); + } + + return contents; +} + +function createInstanceComponentBlock( + component: CommonStorage.Component, functionData: FunctionData): ToolboxItems.Block { + const extraState: CallPythonFunctionExtraState = { + functionKind: FunctionKind.INSTANCE_COMPONENT, + returnType: functionData.returnType, + args: [], + tooltip: functionData.tooltip, + importModule: '', + componentClassName: component.className, + componentName: component.name, + componentBlockId: component.blockId, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_COMPONENT_NAME] = component.name; + fields[FIELD_FUNCTION_NAME] = functionData.functionName; + const inputs: {[key: string]: any} = {}; + // For INSTANCE_COMPONENT functions, the 0 argument is 'self', but + // self is represented by the FIELD_COMPONENT_NAME field. + // We don't include the arg for self. + const argsWithoutSelf = functionData.args.slice(1); + processArgs(argsWithoutSelf, extraState, inputs); + return createBlock(extraState, fields, inputs); +} + +export function addInstanceRobotBlocks( + methods: CommonStorage.Method[], + contents: ToolboxItems.ContentsType[]) { + methods.forEach(method => { + contents.push(createInstanceRobotBlock(method)); + }); +} + +function createInstanceRobotBlock(method: CommonStorage.Method): ToolboxItems.Block { + const extraState: CallPythonFunctionExtraState = { + functionKind: FunctionKind.INSTANCE_ROBOT, + returnType: method.returnType, + actualFunctionName: method.pythonName, + args: [], + classMethodDefBlockId: method.blockId, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_FUNCTION_NAME] = method.visibleName; + const inputs: {[key: string]: any} = {}; + // Convert method.args from CommonStorage.MethodArg[] to ArgData[]. + const args: ArgData[] = []; + // We don't include the arg for the self argument. + for (let i = 1; i < method.args.length; i++) { + args.push({ + name: method.args[i].name, + type: method.args[i].type, + defaultValue: '', + }); } + processArgs(args, extraState, inputs); + return createBlock(extraState, fields, inputs); } diff --git a/src/blocks/mrc_class_method_def.ts b/src/blocks/mrc_class_method_def.ts index 55423e60..14de03f5 100644 --- a/src/blocks/mrc_class_method_def.ts +++ b/src/blocks/mrc_class_method_def.ts @@ -27,12 +27,17 @@ import { Order } from 'blockly/python'; import { ExtendedPythonGenerator } from '../editor/extended_python_generator'; import * as commonStorage from '../storage/common_storage'; import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_function' +import * as toolboxItems from '../toolbox/items'; +import { getClassData } from './utils/python'; +import { FunctionData } from './utils/python_json_types'; import { findConnectedBlocksOfType } from './utils/find_connected_blocks'; import { BLOCK_NAME as MRC_GET_PARAMETER_BLOCK_NAME } from './mrc_get_parameter'; import { MUTATOR_BLOCK_NAME, PARAM_CONTAINER_BLOCK_NAME, MethodMutatorArgBlock } from './mrc_param_container' export const BLOCK_NAME = 'mrc_class_method_def'; +const FIELD_METHOD_NAME = 'NAME'; + export type Parameter = { name: string, type?: string, @@ -88,7 +93,7 @@ const CLASS_METHOD_DEF = { */ init: function (this: ClassMethodDefBlock): void { this.appendDummyInput("TITLE") - .appendField('', 'NAME'); + .appendField('', FIELD_METHOD_NAME); this.setOutput(false); this.setStyle(MRC_STYLE_FUNCTIONS); this.appendStatementInput('STACK').appendField(''); @@ -143,27 +148,27 @@ const CLASS_METHOD_DEF = { }); }); this.updateBlock_(); - mutateMethodCallers(this.workspace, this.getFieldValue('NAME'), this.saveExtraState()); + mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_METHOD_NAME), this.saveExtraState()); }, /** * Update the block to reflect the newly loaded extra state. */ updateBlock_: function (this: ClassMethodDefBlock): void { - const name = this.getFieldValue('NAME'); + const name = this.getFieldValue(FIELD_METHOD_NAME); const input = this.getInput('TITLE'); if (!input) { return; } - input.removeField('NAME'); + input.removeField(FIELD_METHOD_NAME); if (this.mrcCanChangeSignature) { const nameField = new Blockly.FieldTextInput(name); - input.insertFieldAt(0, nameField, 'NAME'); + input.insertFieldAt(0, nameField, FIELD_METHOD_NAME); this.setMutator(new Blockly.icons.MutatorIcon([MUTATOR_BLOCK_NAME], this)); nameField.setValidator(this.mrcNameFieldValidator.bind(this, nameField)); } else { - input.insertFieldAt(0, createFieldNonEditableText(name), 'NAME'); + input.insertFieldAt(0, createFieldNonEditableText(name), FIELD_METHOD_NAME); //Case because a current bug in blockly where it won't allow passing null to Blockly.Block.setMutator makes it necessary. (this as Blockly.BlockSvg).setMutator(null); } @@ -189,7 +194,7 @@ const CLASS_METHOD_DEF = { paramBlock.nextConnection && paramBlock.nextConnection.targetBlock(); } this.mrcUpdateParams(); - mutateMethodCallers(this.workspace, this.getFieldValue('NAME'), this.saveExtraState()); + mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_METHOD_NAME), this.saveExtraState()); }, decompose: function (this: ClassMethodDefBlock, workspace: Blockly.Workspace) { // This is a special sub-block that only gets created in the mutator UI. @@ -268,7 +273,7 @@ const CLASS_METHOD_DEF = { return this.mrcCanChangeSignature; }, getMethodName: function (this: ClassMethodDefBlock): string { - return this.getFieldValue('NAME'); + return this.getFieldValue(FIELD_METHOD_NAME); }, }; @@ -315,7 +320,7 @@ function isMethodNameUsed( if (block === opt_exclude) { continue; } - if (nameLowerCase === block.getFieldValue('NAME').toLowerCase()) { + if (nameLowerCase === block.getFieldValue(FIELD_METHOD_NAME).toLowerCase()) { return true; } const classMethodDefBlock = block as ClassMethodDefBlock; @@ -335,7 +340,7 @@ export const pythonFromBlock = function ( block: ClassMethodDefBlock, generator: ExtendedPythonGenerator, ) { - const blocklyName = block.mrcPythonMethodName ? block.mrcPythonMethodName : block.getFieldValue('NAME'); + const blocklyName = block.mrcPythonMethodName ? block.mrcPythonMethodName : block.getFieldValue(FIELD_METHOD_NAME); const funcName = generator.getProcedureName(blocklyName); @@ -415,7 +420,7 @@ export const pythonFromBlock = function ( // Update the mrcMethod. block.mrcMethod = { blockId: block.id, - visibleName: block.getFieldValue('NAME'), + visibleName: block.getFieldValue(FIELD_METHOD_NAME), pythonName: funcName, returnType: block.mrcReturnType, args: [{ @@ -433,3 +438,58 @@ export const pythonFromBlock = function ( return ''; } + +// Functions used for creating blocks for the toolbox. + +export function createCustomMethodBlock(): toolboxItems.Block { + const extraState: ClassMethodDefExtraState = { + canChangeSignature: true, + canBeCalledWithinClass: true, + canBeCalledOutsideClass: true, + returnType: 'None', + params: [], + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_METHOD_NAME] = 'my_method'; + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, null); +} + +export function getBaseClassBlocks( + baseClassName: string): toolboxItems.Block[] { + const blocks: toolboxItems.Block[] = []; + const classData = getClassData(baseClassName); + if (classData) { + classData.instanceMethods.forEach(functionData => { + blocks.push(createClassMethodDefBlock( + functionData, + /* canChangeSignature */ false, + /* canBeCalledWithinClass */ false, + /* canBeCalledOutsideClass */ false, + )); + }); + } + return blocks; +} + +function createClassMethodDefBlock( + functionData: FunctionData, + canChangeSignature: boolean, + canBeCalledWithinClass: boolean, + canBeCalledOutsideClass: boolean): toolboxItems.Block { + const extraState: ClassMethodDefExtraState = { + canChangeSignature, + canBeCalledWithinClass, + canBeCalledOutsideClass, + returnType: functionData.returnType, + params: [], + }; + for (let i = 1; i < functionData.args.length; i++) { + extraState.params.push({ + 'name': functionData.args[i].name, + 'type': functionData.args[i].type, + }); + } + const fields: {[key: string]: any} = {}; + fields[FIELD_METHOD_NAME] = functionData.functionName; + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, null); +} diff --git a/src/blocks/mrc_event.ts b/src/blocks/mrc_event.ts index 59dca05d..c16c78c3 100644 --- a/src/blocks/mrc_event.ts +++ b/src/blocks/mrc_event.ts @@ -27,10 +27,13 @@ import { ExtendedPythonGenerator } from '../editor/extended_python_generator'; import { MUTATOR_BLOCK_NAME, PARAM_CONTAINER_BLOCK_NAME, MethodMutatorArgBlock } from './mrc_param_container' import * as ChangeFramework from './utils/change_framework'; import { BLOCK_NAME as MRC_MECHANISM_COMPONENT_HOLDER } from './mrc_mechanism_component_holder'; +import * as toolboxItems from '../toolbox/items'; export const BLOCK_NAME = 'mrc_event'; export const OUTPUT_NAME = 'mrc_event'; +const FIELD_EVENT_NAME = 'NAME'; + export type Parameter = { name: string, type?: string, @@ -54,7 +57,7 @@ const EVENT = { init: function (this: EventBlock): void { this.setStyle(MRC_STYLE_EVENTS); this.appendDummyInput("TITLE") - .appendField(new Blockly.FieldTextInput('my_event'), 'NAME'); + .appendField(new Blockly.FieldTextInput('my_event'), FIELD_EVENT_NAME); this.setPreviousStatement(true, OUTPUT_NAME); this.setNextStatement(true, OUTPUT_NAME); this.setMutator(new Blockly.icons.MutatorIcon([MUTATOR_BLOCK_NAME], this)); @@ -99,15 +102,15 @@ const EVENT = { * Update the block to reflect the newly loaded extra state. */ updateBlock_: function (this: EventBlock): void { - const name = this.getFieldValue('NAME'); + const name = this.getFieldValue(FIELD_EVENT_NAME); const input = this.getInput('TITLE'); if (!input) { return; } - input.removeField('NAME'); + input.removeField(FIELD_EVENT_NAME); const nameField = new Blockly.FieldTextInput(name); - input.insertFieldAt(0, nameField, 'NAME'); + input.insertFieldAt(0, nameField, FIELD_EVENT_NAME); this.setMutator(new Blockly.icons.MutatorIcon([MUTATOR_BLOCK_NAME], this)); // nameField.setValidator(this.mrcNameFieldValidator.bind(this, nameField)); @@ -132,7 +135,7 @@ const EVENT = { paramBlock.nextConnection && paramBlock.nextConnection.targetBlock(); } this.mrcUpdateParams(); - //mutateMethodCallers(this.workspace, this.getFieldValue('NAME'), this.saveExtraState()); + //mutateMethodCallers(this.workspace, this.getFieldValue(FIELD_EVENT_NAME), this.saveExtraState()); }, decompose: function (this: EventBlock, workspace: Blockly.Workspace) { // This is a special sub-block that only gets created in the mutator UI. @@ -206,3 +209,14 @@ export const pythonFromBlock = function ( //TODO (Alan): What should this do here?? return ''; } + +// Functions used for creating blocks for the toolbox. + +export function createCustomEventBlock(): toolboxItems.Block { + const extraState: EventExtraState = { + params: [], + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_EVENT_NAME] = 'my_event'; + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, null); +} diff --git a/src/blocks/mrc_get_python_enum_value.ts b/src/blocks/mrc_get_python_enum_value.ts index 4b4c41dc..660225c5 100644 --- a/src/blocks/mrc_get_python_enum_value.ts +++ b/src/blocks/mrc_get_python_enum_value.ts @@ -50,30 +50,6 @@ export function initializeEnum( PythonEnumTooltips[enumClassName] = tooltips; } -// Functions used for creating blocks for the toolbox. - -export function addEnumBlocks(enums: EnumData[], contents: toolboxItems.ContentsType[]) { - for (const enumData of enums) { - for (const enumValue of enumData.enumValues) { - const block = createEnumBlock(enumValue, enumData); - contents.push(block); - } - } -} - -function createEnumBlock(enumValue: string, enumData: EnumData): toolboxItems.Block { - const extraState: GetPythonEnumValueExtraState = { - enumType: enumData.enumClassName, - importModule: enumData.moduleName, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_ENUM_CLASS_NAME] = enumData.enumClassName; - fields[FIELD_ENUM_VALUE] = enumValue; - return new toolboxItems.Block(BLOCK_NAME, extraState, fields, null); -} - -//.............................................................................. - type GetPythonEnumValueBlock = Blockly.Block & GetPythonEnumValueMixin; interface GetPythonEnumValueMixin extends GetPythonEnumValueMixinType { mrcEnumType: string, @@ -175,3 +151,25 @@ export const pythonFromBlock = function( const code = enumClassName + '.' + enumValue; return [code, Order.MEMBER]; }; + +// Functions used for creating blocks for the toolbox. + +export function addEnumBlocks(enums: EnumData[], contents: toolboxItems.ContentsType[]) { + for (const enumData of enums) { + for (const enumValue of enumData.enumValues) { + const block = createEnumBlock(enumValue, enumData); + contents.push(block); + } + } +} + +function createEnumBlock(enumValue: string, enumData: EnumData): toolboxItems.Block { + const extraState: GetPythonEnumValueExtraState = { + enumType: enumData.enumClassName, + importModule: enumData.moduleName, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_ENUM_CLASS_NAME] = enumData.enumClassName; + fields[FIELD_ENUM_VALUE] = enumValue; + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, null); +} diff --git a/src/blocks/mrc_get_python_variable.ts b/src/blocks/mrc_get_python_variable.ts index bbdc824f..ee4533e8 100644 --- a/src/blocks/mrc_get_python_variable.ts +++ b/src/blocks/mrc_get_python_variable.ts @@ -80,108 +80,6 @@ export function initializeInstanceVariableGetter( PythonVariableGetterTooltips[key] = tooltips; } -// Functions used for creating blocks for the toolbox. - -export function addModuleVariableBlocks( - moduleName: string, - varsByType: {[key: string]: VariableGettersAndSetters}, - contents: toolboxItems.ContentsType[]) { - addModuleOrClassVariableBlocks( - VariableKind.MODULE, moduleName, moduleName, varsByType, contents); -} - -export function addClassVariableBlocks( - importModule: string, - className: string, - varsByType: {[key: string]: VariableGettersAndSetters}, - contents: toolboxItems.ContentsType[]) { - addModuleOrClassVariableBlocks( - VariableKind.CLASS, importModule, className, varsByType, contents); -} - -function addModuleOrClassVariableBlocks( - varKind: VariableKind, - importModule: string, - moduleOrClassName: string, - varsByType: {[key: string]: VariableGettersAndSetters}, - contents: toolboxItems.ContentsType[]) { - for (const varType in varsByType) { - const variableGettersAndSetters = varsByType[varType]; - for (let i = 0; i < variableGettersAndSetters.varNamesForGetter.length; i++) { - const varName = variableGettersAndSetters.varNamesForGetter[i]; - const getterBlock = createModuleOrClassVariableGetterBlock( - varKind, importModule, moduleOrClassName, varType, varName); - contents.push(getterBlock); - if (variableGettersAndSetters.varNamesForSetter.includes(varName)) { - const setterBlock = createModuleOrClassVariableSetterBlock( - VariableKind.CLASS, importModule, moduleOrClassName, varType, varName); - contents.push(setterBlock); - } - } - } -} - -function createModuleOrClassVariableGetterBlock( - varKind: VariableKind, - importModule: string, - moduleOrClassName: string, - varType: string, - varName: string): toolboxItems.Block { - const extraState: GetPythonVariableExtraState = { - varKind: varKind, - moduleOrClassName: moduleOrClassName, - varType: varType, - importModule: importModule, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_MODULE_OR_CLASS_NAME] = moduleOrClassName; - fields[FIELD_VARIABLE_NAME] = varName; - return new toolboxItems.Block(BLOCK_NAME, extraState, fields, null); -} - -export function addInstanceVariableBlocks( - className: string, - varsByType: {[key: string]: VariableGettersAndSetters}, - contents: toolboxItems.ContentsType[]) { - for (const varType in varsByType) { - const variableGettersAndSetters = varsByType[varType]; - for (let i = 0; i < variableGettersAndSetters.varNamesForGetter.length; i++) { - const varName = variableGettersAndSetters.varNamesForGetter[i]; - const getterBlock = createInstanceVariableGetterBlock(className, varType, varName); - contents.push(getterBlock); - if (variableGettersAndSetters.varNamesForSetter.includes(varName)) { - const setterBlock = createInstanceVariableSetterBlock(className, varType, varName); - contents.push(setterBlock); - } - } - } -} - -function createInstanceVariableGetterBlock( - className: string, - varType: string, - varName: string): toolboxItems.Block { - const selfLabel = variable.getSelfArgName(className); - const extraState: GetPythonVariableExtraState = { - varKind: VariableKind.INSTANCE, - moduleOrClassName: className, - varType: varType, - selfLabel: selfLabel, - selfType: className, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_MODULE_OR_CLASS_NAME] = className; - fields[FIELD_VARIABLE_NAME] = varName; - const inputs: {[key: string]: any} = {}; - const selfVarName = variable.varNameForType(className); - if (selfVarName) { - inputs['SELF'] = variable.createVariableGetterBlockValue(selfVarName); - } - return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); -} - -//.............................................................................. - type GetPythonVariableBlock = Blockly.Block & GetPythonVariableMixin; interface GetPythonVariableMixin extends GetPythonVariableMixinType { mrcVarKind: VariableKind, @@ -391,3 +289,103 @@ export const pythonFromBlock = function( throw new Error('mrcVarKind must be "module", "class", or "instance".') } }; + +// Functions used for creating blocks for the toolbox. + +export function addModuleVariableBlocks( + moduleName: string, + varsByType: {[key: string]: VariableGettersAndSetters}, + contents: toolboxItems.ContentsType[]) { + addModuleOrClassVariableBlocks( + VariableKind.MODULE, moduleName, moduleName, varsByType, contents); +} + +export function addClassVariableBlocks( + importModule: string, + className: string, + varsByType: {[key: string]: VariableGettersAndSetters}, + contents: toolboxItems.ContentsType[]) { + addModuleOrClassVariableBlocks( + VariableKind.CLASS, importModule, className, varsByType, contents); +} + +function addModuleOrClassVariableBlocks( + varKind: VariableKind, + importModule: string, + moduleOrClassName: string, + varsByType: {[key: string]: VariableGettersAndSetters}, + contents: toolboxItems.ContentsType[]) { + for (const varType in varsByType) { + const variableGettersAndSetters = varsByType[varType]; + for (let i = 0; i < variableGettersAndSetters.varNamesForGetter.length; i++) { + const varName = variableGettersAndSetters.varNamesForGetter[i]; + const getterBlock = createModuleOrClassVariableGetterBlock( + varKind, importModule, moduleOrClassName, varType, varName); + contents.push(getterBlock); + if (variableGettersAndSetters.varNamesForSetter.includes(varName)) { + const setterBlock = createModuleOrClassVariableSetterBlock( + VariableKind.CLASS, importModule, moduleOrClassName, varType, varName); + contents.push(setterBlock); + } + } + } +} + +function createModuleOrClassVariableGetterBlock( + varKind: VariableKind, + importModule: string, + moduleOrClassName: string, + varType: string, + varName: string): toolboxItems.Block { + const extraState: GetPythonVariableExtraState = { + varKind: varKind, + moduleOrClassName: moduleOrClassName, + varType: varType, + importModule: importModule, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_MODULE_OR_CLASS_NAME] = moduleOrClassName; + fields[FIELD_VARIABLE_NAME] = varName; + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, null); +} + +export function addInstanceVariableBlocks( + className: string, + varsByType: {[key: string]: VariableGettersAndSetters}, + contents: toolboxItems.ContentsType[]) { + for (const varType in varsByType) { + const variableGettersAndSetters = varsByType[varType]; + for (let i = 0; i < variableGettersAndSetters.varNamesForGetter.length; i++) { + const varName = variableGettersAndSetters.varNamesForGetter[i]; + const getterBlock = createInstanceVariableGetterBlock(className, varType, varName); + contents.push(getterBlock); + if (variableGettersAndSetters.varNamesForSetter.includes(varName)) { + const setterBlock = createInstanceVariableSetterBlock(className, varType, varName); + contents.push(setterBlock); + } + } + } +} + +function createInstanceVariableGetterBlock( + className: string, + varType: string, + varName: string): toolboxItems.Block { + const selfLabel = variable.getSelfArgName(className); + const extraState: GetPythonVariableExtraState = { + varKind: VariableKind.INSTANCE, + moduleOrClassName: className, + varType: varType, + selfLabel: selfLabel, + selfType: className, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_MODULE_OR_CLASS_NAME] = className; + fields[FIELD_VARIABLE_NAME] = varName; + const inputs: {[key: string]: any} = {}; + const selfVarName = variable.varNameForType(className); + if (selfVarName) { + inputs['SELF'] = variable.createVariableGetterBlockValue(selfVarName); + } + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); +} diff --git a/src/blocks/mrc_set_python_variable.ts b/src/blocks/mrc_set_python_variable.ts index a59aecde..f7e86aa1 100644 --- a/src/blocks/mrc_set_python_variable.ts +++ b/src/blocks/mrc_set_python_variable.ts @@ -78,60 +78,6 @@ export function initializeInstanceVariableSetter( PythonVariableSetterTooltips[key] = tooltips; } -// Functions used for creating blocks for the toolbox. - -export function createModuleOrClassVariableSetterBlock( - varKind: VariableKind, - importModule: string, - moduleOrClassName: string, - varType: string, - varName: string): toolboxItems.Block { - const extraState: SetPythonVariableExtraState = { - varKind: varKind, - moduleOrClassName: moduleOrClassName, - varType: varType, - importModule: importModule, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_MODULE_OR_CLASS_NAME] = moduleOrClassName; - fields[FIELD_VARIABLE_NAME] = varName; - const inputs: {[key: string]: any} = {}; - const valueVarName = variable.varNameForType(varType); - if (valueVarName) { - inputs['VALUE'] = variable.createVariableGetterBlockValue(valueVarName); - } - return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); -} - -export function createInstanceVariableSetterBlock( - className: string, - varType: string, - varName: string): toolboxItems.Block { - const selfLabel = variable.getSelfArgName(className); - const extraState: SetPythonVariableExtraState = { - varKind: VariableKind.INSTANCE, - moduleOrClassName: className, - varType: varType, - selfLabel: selfLabel, - selfType: className, - }; - const fields: {[key: string]: any} = {}; - fields[FIELD_MODULE_OR_CLASS_NAME] = className; - fields[FIELD_VARIABLE_NAME] = varName; - const inputs: {[key: string]: any} = {}; - const valueVarName = variable.varNameForType(varType); - if (valueVarName) { - inputs['VALUE'] = variable.createVariableGetterBlockValue(valueVarName); - } - const selfVarName = variable.varNameForType(className); - if (selfVarName) { - inputs['SELF'] = variable.createVariableGetterBlockValue(selfVarName); - } - return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); -} - -//.............................................................................. - type SetPythonVariableBlock = Blockly.Block & SetPythonVariableMixin; interface SetPythonVariableMixin extends SetPythonVariableMixinType { mrcVarKind: VariableKind, @@ -343,3 +289,55 @@ export const pythonFromBlock = function( throw new Error('mrcVarKind must be "module", "class", or "instance".') } }; + +// Functions used for creating blocks for the toolbox. + +export function createModuleOrClassVariableSetterBlock( + varKind: VariableKind, + importModule: string, + moduleOrClassName: string, + varType: string, + varName: string): toolboxItems.Block { + const extraState: SetPythonVariableExtraState = { + varKind: varKind, + moduleOrClassName: moduleOrClassName, + varType: varType, + importModule: importModule, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_MODULE_OR_CLASS_NAME] = moduleOrClassName; + fields[FIELD_VARIABLE_NAME] = varName; + const inputs: {[key: string]: any} = {}; + const valueVarName = variable.varNameForType(varType); + if (valueVarName) { + inputs['VALUE'] = variable.createVariableGetterBlockValue(valueVarName); + } + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); +} + +export function createInstanceVariableSetterBlock( + className: string, + varType: string, + varName: string): toolboxItems.Block { + const selfLabel = variable.getSelfArgName(className); + const extraState: SetPythonVariableExtraState = { + varKind: VariableKind.INSTANCE, + moduleOrClassName: className, + varType: varType, + selfLabel: selfLabel, + selfType: className, + }; + const fields: {[key: string]: any} = {}; + fields[FIELD_MODULE_OR_CLASS_NAME] = className; + fields[FIELD_VARIABLE_NAME] = varName; + const inputs: {[key: string]: any} = {}; + const valueVarName = variable.varNameForType(varType); + if (valueVarName) { + inputs['VALUE'] = variable.createVariableGetterBlockValue(valueVarName); + } + const selfVarName = variable.varNameForType(className); + if (selfVarName) { + inputs['SELF'] = variable.createVariableGetterBlockValue(selfVarName); + } + return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null); +} diff --git a/src/toolbox/event_category.ts b/src/toolbox/event_category.ts index 2e2ecc9d..f388e17e 100644 --- a/src/toolbox/event_category.ts +++ b/src/toolbox/event_category.ts @@ -26,6 +26,7 @@ import * as commonStorage from '../storage/common_storage'; import { EventBlock } from '../blocks/mrc_event'; import { MRC_CATEGORY_STYLE_METHODS } from '../themes/styles'; import { RETURN_TYPE_NONE, FunctionKind } from '../blocks/mrc_call_python_function'; +import { createCustomEventBlock } from '../blocks/mrc_event'; const CUSTOM_CATEGORY_EVENTS = 'EVENTS'; @@ -50,29 +51,13 @@ export class EventsCategory { public eventsFlyout(workspace: Blockly.WorkspaceSvg) { const contents: toolboxItems.ContentsType[] = []; - // Add blocks for defining any methods that can be defined in the current - // module. For example, if the current module is an OpMode, add blocks to - // define the methods declared in the OpMode class. - if (this.currentModule) { - // Collect the method names for mrc_class_method_def blocks that are - // already in the blockly workspace. - const eventNamesAlreadyUsed: string[] = []; - workspace.getBlocksByType('mrc_event', false).forEach((block) => { - eventNamesAlreadyUsed.push(block.getFieldValue('NAME')); - }); - } - - // Add a block that lets the user define a new method. + // Add a block that lets the user define a new event. contents.push( { kind: 'label', text: 'Custom Events', }, - { - kind: 'block', - type: 'mrc_event', - fields: {NAME: "my_event"}, - }); + createCustomEventBlock()); // For each mrc_class_method_def block in the blockly workspace, check if it // can be called from within the class, and if so, add a diff --git a/src/toolbox/hardware_category.ts b/src/toolbox/hardware_category.ts index 5367f8ee..12a5258d 100644 --- a/src/toolbox/hardware_category.ts +++ b/src/toolbox/hardware_category.ts @@ -23,7 +23,7 @@ import * as Blockly from 'blockly/core'; import * as commonStorage from '../storage/common_storage'; import * as toolboxItems from './items'; import { getAllPossibleMechanisms } from './blocks_mechanisms'; -import * as Component from '../blocks/mrc_component'; +import { getAllPossibleComponents } from '../blocks/mrc_component'; import { getInstanceComponentBlocks, addInstanceRobotBlocks } from '../blocks/mrc_call_python_function'; import { Editor } from '../editor/editor'; @@ -274,7 +274,7 @@ function getComponentsBlocks(hideParams : boolean): toolboxItems.Category { contents.push({ kind: 'category', name: Blockly.Msg['MRC_CATEGORY_ADD_COMPONENT'], - contents: Component.getAllPossibleComponents(hideParams) + contents: getAllPossibleComponents(hideParams) }); // Get components from the current workspace. diff --git a/src/toolbox/mechanism_class_methods.ts b/src/toolbox/mechanism_class_methods.ts deleted file mode 100644 index aeb6cfbe..00000000 --- a/src/toolbox/mechanism_class_methods.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @author lizlooney@google.com (Liz Looney) - */ - -import * as toolboxItems from './items'; - - -/** - * The blocks that define the methods for the Mechanism class. - * - * These blocks will be added (if appropriate) to the Methods category in the - * toolbox. - */ -export const mechanism_class_blocks: toolboxItems.Block[] = [ - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'start', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - }, - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'update', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - }, - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'stop', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - }, -]; diff --git a/src/toolbox/methods_category.ts b/src/toolbox/methods_category.ts index bb56d833..d39c4487 100644 --- a/src/toolbox/methods_category.ts +++ b/src/toolbox/methods_category.ts @@ -24,11 +24,8 @@ import * as Blockly from 'blockly/core'; import * as toolboxItems from './items'; import * as commonStorage from '../storage/common_storage'; import { MRC_CATEGORY_STYLE_METHODS } from '../themes/styles' -import { mechanism_class_blocks } from './mechanism_class_methods'; -import { opmode_class_blocks } from './opmode_class_methods'; -import { robot_class_blocks } from './robot_class_methods'; -import { ClassMethodDefBlock } from '../blocks/mrc_class_method_def' import { addInstanceWithinBlocks } from '../blocks/mrc_call_python_function' +import { createCustomMethodBlock, getBaseClassBlocks } from '../blocks/mrc_class_method_def'; import { Editor } from '../editor/editor'; @@ -43,6 +40,9 @@ export const getCategory = () => ({ export class MethodsCategory { private currentModule: commonStorage.Module | null = null; + private robotClassBlocks = getBaseClassBlocks('blocks_base_classes.robot_base.RobotBase'); + private mechanismClassBlocks = getBaseClassBlocks('blocks_base_classes.mechanism.Mechanism'); + private opmodeClassBlocks = getBaseClassBlocks('blocks_base_classes.opmode.OpMode'); constructor(blocklyWorkspace: Blockly.WorkspaceSvg) { blocklyWorkspace.registerToolboxCategoryCallback(CUSTOM_CATEGORY_METHODS, this.methodsFlyout.bind(this)); @@ -68,17 +68,17 @@ export class MethodsCategory { if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_ROBOT) { // Add the methods for a Robot. this.addClassBlocksForCurrentModule( - 'More Robot Methods', robot_class_blocks, + 'More Robot Methods', this.robotClassBlocks, methodNamesAlreadyOverridden, contents); } else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_MECHANISM) { // Add the methods for a Mechanism. this.addClassBlocksForCurrentModule( - 'More Mechanism Methods', mechanism_class_blocks, + 'More Mechanism Methods', this.mechanismClassBlocks, methodNamesAlreadyOverridden, contents); } else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) { // Add the methods for an OpMode. this.addClassBlocksForCurrentModule( - 'More OpMode Methods', opmode_class_blocks, + 'More OpMode Methods', this.opmodeClassBlocks, methodNamesAlreadyOverridden, contents); } } @@ -90,19 +90,7 @@ export class MethodsCategory { kind: 'label', text: 'Custom Methods', }, - { - kind: 'block', - type: 'mrc_class_method_def', - fields: {NAME: "my_method"}, - extraState: { - canChangeSignature: true, - canBeCalledWithinClass: true, - canBeCalledOutsideClass: true, - canDelete: true, - returnType: 'None', - params: [], - }, - } + createCustomMethodBlock(), ); // Get blocks for calling methods defined in the current workspace. @@ -140,4 +128,5 @@ export class MethodsCategory { } }); } + } diff --git a/src/toolbox/opmode_class_methods.ts b/src/toolbox/opmode_class_methods.ts deleted file mode 100644 index 068578f5..00000000 --- a/src/toolbox/opmode_class_methods.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @author lizlooney@google.com (Liz Looney) - */ - -import * as toolboxItems from './items'; - - -/** - * The blocks that define the methods for the OpMode class. - * - * These blocks will be added (if appropriate) to the Methods category in the - * toolbox. - */ -export const opmode_class_blocks: toolboxItems.Block[] = [ - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'start', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - }, - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'loop', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: false, - returnType: 'None', - params: [], - }, - }, - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'stop', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - }, -]; diff --git a/src/toolbox/robot_class_methods.ts b/src/toolbox/robot_class_methods.ts deleted file mode 100644 index 983e55ab..00000000 --- a/src/toolbox/robot_class_methods.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @author lizlooney@google.com (Liz Looney) - */ - -import * as toolboxItems from './items'; - - -/** - * The blocks that define the methods for the Robot class. - * - * These blocks will be added (if appropriate) to the Methods category in the - * toolbox. - */ -export const robot_class_blocks: toolboxItems.Block[] = [ - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'start', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - }, - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'update', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - }, - { - kind: 'block', - type: 'mrc_class_method_def', - fields: { - 'NAME': 'stop', - }, - extraState: { - canChangeSignature: false, - canBeCalledWithinClass: false, - canBeCalledOutsideClass: false, - canDelete: true, - returnType: 'None', - params: [], - }, - },];