2222import * as Blockly from 'blockly' ;
2323import { MRC_STYLE_CLASS_BLOCKS } from '../themes/styles' ;
2424import { createFieldNonEditableText } from '../fields/FieldNonEditableText'
25+ import { createFieldFlydown } from '../fields/field_flydown' ;
2526import * as ChangeFramework from './utils/change_framework'
2627import { getLegalName } from './utils/python' ;
2728import { Order } from 'blockly/python' ;
@@ -31,7 +32,7 @@ import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_func
3132export const BLOCK_NAME = 'mrc_class_method_def' ;
3233
3334export const MUTATOR_BLOCK_NAME = 'methods_mutatorarg' ;
34- const PARAM_CONTAINER_BLOCK_NAME = 'method_param_container' ;
35+ const PARAM_CONTAINER_BLOCK_NAME = 'method_param_container' ;
3536
3637export type Parameter = {
3738 name : string ,
@@ -87,8 +88,7 @@ const CLASS_METHOD_DEF = {
8788 */
8889 init : function ( this : ClassMethodDefBlock ) : void {
8990 this . appendDummyInput ( "TITLE" )
90- . appendField ( '' , 'NAME' )
91- . appendField ( '' , 'PARAMS' ) ;
91+ . appendField ( '' , 'NAME' ) ;
9292 this . setOutput ( false ) ;
9393 this . setStyle ( MRC_STYLE_CLASS_BLOCKS ) ;
9494 this . appendStatementInput ( 'STACK' ) . appendField ( '' ) ;
@@ -147,11 +147,11 @@ const CLASS_METHOD_DEF = {
147147 updateBlock_ : function ( this : ClassMethodDefBlock ) : void {
148148 const name = this . getFieldValue ( 'NAME' ) ;
149149 const input = this . getInput ( 'TITLE' ) ;
150- if ( ! input ) {
150+ if ( ! input ) {
151151 return ;
152152 }
153153 input . removeField ( 'NAME' ) ;
154-
154+
155155 if ( this . mrcCanChangeSignature ) {
156156 const nameField = new Blockly . FieldTextInput ( name ) ;
157157 input . insertFieldAt ( 0 , nameField , 'NAME' ) ;
@@ -161,7 +161,7 @@ const CLASS_METHOD_DEF = {
161161 else {
162162 input . insertFieldAt ( 0 , createFieldNonEditableText ( name ) , 'NAME' ) ;
163163 //Case because a current bug in blockly where it won't allow passing null to Blockly.Block.setMutator makes it necessary.
164- ( this as Blockly . BlockSvg ) . setMutator ( null ) ;
164+ ( this as Blockly . BlockSvg ) . setMutator ( null ) ;
165165 }
166166 this . mrcUpdateParams ( ) ;
167167 } ,
@@ -171,9 +171,9 @@ const CLASS_METHOD_DEF = {
171171
172172 let paramBlock = containerBlock . getInputTargetBlock ( 'STACK' ) ;
173173 while ( paramBlock && ! paramBlock . isInsertionMarker ( ) ) {
174- const param : Parameter = {
175- name : paramBlock . getFieldValue ( 'NAME' ) ,
176- type : ''
174+ const param : Parameter = {
175+ name : paramBlock . getFieldValue ( 'NAME' ) ,
176+ type : ''
177177 }
178178 this . mrcParameters . push ( param ) ;
179179 paramBlock =
@@ -201,40 +201,41 @@ const CLASS_METHOD_DEF = {
201201 }
202202 return topBlock ;
203203 } ,
204- mrcUpdateParams : function ( this : ClassMethodDefBlock ) {
205- let paramString = '' ;
206- if ( this . mrcParameters . length > 0 ) {
207- this . mrcParameters . forEach ( ( param ) => {
208- if ( paramString != '' ) {
209- paramString += ', ' ;
210- }
211- paramString += param . name ;
212- } ) ;
213- paramString = Blockly . Msg [ 'PROCEDURES_BEFORE_PARAMS' ] + ' ' + paramString ;
214- }
215- // The params field is deterministic based on the mutation,
216- // no need to fire a change event.
217- Blockly . Events . disable ( ) ;
218- try {
219- this . setFieldValue ( paramString , 'PARAMS' ) ;
220- } finally {
221- Blockly . Events . enable ( ) ;
204+ mrcUpdateParams : function ( this : ClassMethodDefBlock ) {
205+ if ( this . mrcParameters . length > 0 ) {
206+ let input = this . getInput ( 'TITLE' ) ;
207+ if ( input ) {
208+ this . removeParameterFields ( input ) ;
209+ this . mrcParameters . forEach ( ( param ) => {
210+ const paramName = 'PARAM_' + param . name ;
211+ input . appendField ( createFieldFlydown ( param . name , false ) , paramName ) ;
212+ } ) ;
213+ }
222214 }
223215 } ,
216+ removeParameterFields : function ( input : Blockly . Input ) {
217+ const fieldsToRemove = input . fieldRow
218+ . filter ( field => field . name ?. startsWith ( 'PARAM_' ) )
219+ . map ( field => field . name ! ) ;
220+
221+ fieldsToRemove . forEach ( fieldName => {
222+ input . removeField ( fieldName ) ;
223+ } ) ;
224+ } ,
224225 mrcNameFieldValidator ( this : ClassMethodDefBlock , nameField : Blockly . FieldTextInput , name : string ) : string {
225- // When the user changes the method name on the block, clear the mrcPythonMethodName field.
226- this . mrcPythonMethodName = '' ;
227-
228- // Strip leading and trailing whitespace.
229- name = name . trim ( ) ;
230-
231- const legalName = findLegalMethodName ( name , this ) ;
232- const oldName = nameField . getValue ( ) ;
233- if ( oldName && oldName !== name && oldName !== legalName ) {
234- // Rename any callers.
235- renameMethodCallers ( this . workspace , oldName , legalName ) ;
236- }
237- return legalName ;
226+ // When the user changes the method name on the block, clear the mrcPythonMethodName field.
227+ this . mrcPythonMethodName = '' ;
228+
229+ // Strip leading and trailing whitespace.
230+ name = name . trim ( ) ;
231+
232+ const legalName = findLegalMethodName ( name , this ) ;
233+ const oldName = nameField . getValue ( ) ;
234+ if ( oldName && oldName !== name && oldName !== legalName ) {
235+ // Rename any callers.
236+ renameMethodCallers ( this . workspace , oldName , legalName ) ;
237+ }
238+ return legalName ;
238239 } ,
239240} ;
240241
@@ -248,21 +249,21 @@ const CLASS_METHOD_DEF = {
248249 * @returns Non-colliding name.
249250 */
250251function findLegalMethodName ( name : string , block : ClassMethodDefBlock ) : string {
251- if ( block . isInFlyout ) {
252- // Flyouts can have multiple methods called 'my_method'.
253- return name ;
254- }
255- name = name || 'unnamed' ;
256- while ( isMethodNameUsed ( name , block . workspace , block ) ) {
257- // Collision with another method.
258- const r = name . match ( / ^ ( .* ?) ( \d + ) $ / ) ;
259- if ( ! r ) {
260- name += '2' ;
261- } else {
262- name = r [ 1 ] + ( parseInt ( r [ 2 ] ) + 1 ) ;
252+ if ( block . isInFlyout ) {
253+ // Flyouts can have multiple methods called 'my_method'.
254+ return name ;
263255 }
264- }
265- return name ;
256+ name = name || 'unnamed' ;
257+ while ( isMethodNameUsed ( name , block . workspace , block ) ) {
258+ // Collision with another method.
259+ const r = name . match ( / ^ ( .* ?) ( \d + ) $ / ) ;
260+ if ( ! r ) {
261+ name += '2' ;
262+ } else {
263+ name = r [ 1 ] + ( parseInt ( r [ 2 ] ) + 1 ) ;
264+ }
265+ }
266+ return name ;
266267}
267268
268269/**
@@ -276,25 +277,25 @@ function findLegalMethodName(name: string, block: ClassMethodDefBlock): string {
276277 */
277278function isMethodNameUsed (
278279 name : string , workspace : Blockly . Workspace , opt_exclude ?: Blockly . Block ) : boolean {
279- const nameLowerCase = name . toLowerCase ( ) ;
280- for ( const block of workspace . getBlocksByType ( 'mrc_class_method_def' ) ) {
281- if ( block === opt_exclude ) {
282- continue ;
283- }
284- if ( nameLowerCase === block . getFieldValue ( 'NAME' ) . toLowerCase ( ) ) {
285- return true ;
286- }
287- const classMethodDefBlock = block as ClassMethodDefBlock ;
288- if ( classMethodDefBlock . mrcPythonMethodName &&
289- nameLowerCase === classMethodDefBlock . mrcPythonMethodName . toLowerCase ( ) ) {
290- return true ;
280+ const nameLowerCase = name . toLowerCase ( ) ;
281+ for ( const block of workspace . getBlocksByType ( 'mrc_class_method_def' ) ) {
282+ if ( block === opt_exclude ) {
283+ continue ;
284+ }
285+ if ( nameLowerCase === block . getFieldValue ( 'NAME' ) . toLowerCase ( ) ) {
286+ return true ;
287+ }
288+ const classMethodDefBlock = block as ClassMethodDefBlock ;
289+ if ( classMethodDefBlock . mrcPythonMethodName &&
290+ nameLowerCase === classMethodDefBlock . mrcPythonMethodName . toLowerCase ( ) ) {
291+ return true ;
292+ }
291293 }
292- }
293- return false ;
294+ return false ;
294295}
295296
296297const METHOD_PARAM_CONTAINER = {
297- init : function ( this : Blockly . Block ) {
298+ init : function ( this : Blockly . Block ) {
298299 this . appendDummyInput ( "TITLE" ) . appendField ( 'Parameters' ) ;
299300 this . appendStatementInput ( 'STACK' ) ;
300301 this . setStyle ( MRC_STYLE_CLASS_BLOCKS ) ;
@@ -303,12 +304,12 @@ const METHOD_PARAM_CONTAINER = {
303304} ;
304305
305306type MethodMutatorArgBlock = Blockly . Block & MethodMutatorArgMixin & Blockly . BlockSvg ;
306- interface MethodMutatorArgMixin extends MethodMutatorArgMixinType {
307+ interface MethodMutatorArgMixin extends MethodMutatorArgMixinType {
307308
308309}
309310type MethodMutatorArgMixinType = typeof METHODS_MUTATORARG ;
310311
311- function setName ( block : Blockly . BlockSvg ) {
312+ function setName ( block : Blockly . BlockSvg ) {
312313 const parentBlock = ChangeFramework . getParentOfType ( block , PARAM_CONTAINER_BLOCK_NAME ) ;
313314 if ( parentBlock ) {
314315 const variableBlocks = parentBlock ! . getDescendants ( true )
@@ -318,14 +319,14 @@ function setName(block: Blockly.BlockSvg){
318319 otherNames . push ( variableBlock . getFieldValue ( 'NAME' ) ) ;
319320 }
320321 } ) ;
321- const currentName = block . getFieldValue ( 'NAME' ) ;
322+ const currentName = block . getFieldValue ( 'NAME' ) ;
322323 block . setFieldValue ( getLegalName ( currentName , otherNames ) , 'NAME' ) ;
323324 updateMutatorFlyout ( block . workspace ) ;
324325 }
325326}
326327
327328const METHODS_MUTATORARG = {
328- init : function ( this : MethodMutatorArgBlock ) {
329+ init : function ( this : MethodMutatorArgBlock ) {
329330 this . appendDummyInput ( )
330331 . appendField ( new Blockly . FieldTextInput ( Blockly . Procedures . DEFAULT_ARG ) , 'NAME' ) ;
331332 this . setPreviousStatement ( true ) ;
@@ -335,17 +336,17 @@ const METHODS_MUTATORARG = {
335336 ChangeFramework . registerCallback ( MUTATOR_BLOCK_NAME , [ Blockly . Events . BLOCK_MOVE , Blockly . Events . BLOCK_CHANGE ] , this . onBlockChanged ) ;
336337 } ,
337338 onBlockChanged : function ( block : Blockly . BlockSvg , blockEvent : Blockly . Events . BlockBase ) {
338- if ( blockEvent . type == Blockly . Events . BLOCK_MOVE ) {
339+ if ( blockEvent . type == Blockly . Events . BLOCK_MOVE ) {
339340 let blockMoveEvent = blockEvent as Blockly . Events . BlockMove ;
340341 if ( blockMoveEvent . reason ?. includes ( 'connect' ) ) {
341- setName ( block ) ;
342+ setName ( block ) ;
342343 }
343344 }
344- else {
345- if ( blockEvent . type == Blockly . Events . BLOCK_CHANGE ) {
345+ else {
346+ if ( blockEvent . type == Blockly . Events . BLOCK_CHANGE ) {
346347 setName ( block ) ;
347348 }
348- }
349+ }
349350 } ,
350351}
351352
@@ -357,24 +358,24 @@ const METHODS_MUTATORARG = {
357358 * is what is being updated.
358359 */
359360function updateMutatorFlyout ( workspace : Blockly . WorkspaceSvg ) {
360- const usedNames = [ ] ;
361- const blocks = workspace . getBlocksByType ( MUTATOR_BLOCK_NAME , false ) ;
362- for ( let i = 0 , block ; ( block = blocks [ i ] ) ; i ++ ) {
363- usedNames . push ( block . getFieldValue ( 'NAME' ) ) ;
364- }
365- const argValue = Blockly . Variables . generateUniqueNameFromOptions (
366- Blockly . Procedures . DEFAULT_ARG ,
367- usedNames ,
368- ) ;
369- const jsonBlock = {
370- kind : 'block' ,
371- type : MUTATOR_BLOCK_NAME ,
372- fields : {
373- NAME : argValue ,
374- } ,
375- } ;
376-
377- workspace . updateToolbox ( { contents :[ jsonBlock ] } ) ;
361+ const usedNames = [ ] ;
362+ const blocks = workspace . getBlocksByType ( MUTATOR_BLOCK_NAME , false ) ;
363+ for ( let i = 0 , block ; ( block = blocks [ i ] ) ; i ++ ) {
364+ usedNames . push ( block . getFieldValue ( 'NAME' ) ) ;
365+ }
366+ const argValue = Blockly . Variables . generateUniqueNameFromOptions (
367+ Blockly . Procedures . DEFAULT_ARG ,
368+ usedNames ,
369+ ) ;
370+ const jsonBlock = {
371+ kind : 'block' ,
372+ type : MUTATOR_BLOCK_NAME ,
373+ fields : {
374+ NAME : argValue ,
375+ } ,
376+ } ;
377+
378+ workspace . updateToolbox ( { contents : [ jsonBlock ] } ) ;
378379}
379380
380381
@@ -386,37 +387,37 @@ function updateMutatorFlyout(workspace: Blockly.WorkspaceSvg) {
386387 * @internal
387388 */
388389export function mutatorOpenListener ( e : Blockly . Events . Abstract ) {
389- if ( e . type != Blockly . Events . BUBBLE_OPEN ) {
390- return ;
391- }
390+ if ( e . type != Blockly . Events . BUBBLE_OPEN ) {
391+ return ;
392+ }
392393 const bubbleEvent = e as Blockly . Events . BubbleOpen ;
393394 if (
394- ! ( bubbleEvent . bubbleType === 'mutator' && bubbleEvent . isOpen ) ||
395- ! bubbleEvent . blockId
395+ ! ( bubbleEvent . bubbleType === 'mutator' && bubbleEvent . isOpen ) ||
396+ ! bubbleEvent . blockId
396397 ) {
397- return ;
398+ return ;
398399 }
399400 const workspaceId = bubbleEvent . workspaceId ;
400401 const block = Blockly . common
401- . getWorkspaceById ( workspaceId ) !
402- . getBlockById ( bubbleEvent . blockId ) as Blockly . BlockSvg ;
402+ . getWorkspaceById ( workspaceId ) !
403+ . getBlockById ( bubbleEvent . blockId ) as Blockly . BlockSvg ;
403404
404405 if ( block . type !== BLOCK_NAME ) {
405- return ;
406+ return ;
406407 }
407408 const workspace = (
408- block . getIcon ( Blockly . icons . MutatorIcon . TYPE ) as Blockly . icons . MutatorIcon
409+ block . getIcon ( Blockly . icons . MutatorIcon . TYPE ) as Blockly . icons . MutatorIcon
409410 ) . getWorkspace ( ) ! ;
410411
411412 updateMutatorFlyout ( workspace ) ;
412413 ChangeFramework . setup ( workspace ) ;
413- }
414+ }
414415
415416
416- export const setup = function ( ) {
417- Blockly . Blocks [ BLOCK_NAME ] = CLASS_METHOD_DEF ;
418- Blockly . Blocks [ MUTATOR_BLOCK_NAME ] = METHODS_MUTATORARG ;
419- Blockly . Blocks [ PARAM_CONTAINER_BLOCK_NAME ] = METHOD_PARAM_CONTAINER ;
417+ export const setup = function ( ) {
418+ Blockly . Blocks [ BLOCK_NAME ] = CLASS_METHOD_DEF ;
419+ Blockly . Blocks [ MUTATOR_BLOCK_NAME ] = METHODS_MUTATORARG ;
420+ Blockly . Blocks [ PARAM_CONTAINER_BLOCK_NAME ] = METHOD_PARAM_CONTAINER ;
420421} ;
421422
422423export const pythonFromBlock = function (
@@ -457,11 +458,11 @@ export const pythonFromBlock = function (
457458 // After executing the function body, revisit this block for the return.
458459 xfix2 = xfix1 ;
459460 }
460- if ( block . mrcPythonMethodName == '__init__' ) {
461+ if ( block . mrcPythonMethodName == '__init__' ) {
461462 let class_specific = generator . getClassSpecificForInit ( ) ;
462463 branch = generator . INDENT + 'super().__init__(' + class_specific + ')\n' +
463464 generator . defineClassVariables ( ) + branch ;
464- }
465+ }
465466 if ( returnValue ) {
466467 returnValue = generator . INDENT + 'return ' + returnValue + '\n' ;
467468 } else if ( ! branch ) {
0 commit comments