@@ -24,7 +24,6 @@ import * as Blockly from 'blockly';
24
24
import { MRC_STYLE_EVENTS } from '../themes/styles'
25
25
import { ExtendedPythonGenerator } from '../editor/extended_python_generator' ;
26
26
import { MUTATOR_BLOCK_NAME , PARAM_CONTAINER_BLOCK_NAME , MethodMutatorArgBlock } from './mrc_param_container'
27
- import * as ChangeFramework from './utils/change_framework' ;
28
27
import { BLOCK_NAME as MRC_MECHANISM_COMPONENT_HOLDER } from './mrc_mechanism_component_holder' ;
29
28
import * as toolboxItems from '../toolbox/items' ;
30
29
import * as storageModuleContent from '../storage/module_content' ;
@@ -33,13 +32,17 @@ import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_func
33
32
export const BLOCK_NAME = 'mrc_event' ;
34
33
export const OUTPUT_NAME = 'mrc_event' ;
35
34
35
+ const INPUT_TITLE = 'TITLE' ;
36
36
const FIELD_EVENT_NAME = 'NAME' ;
37
+ const FIELD_PARAM_PREFIX = 'PARAM_' ;
37
38
38
39
type Parameter = {
39
40
name : string ,
40
41
type ?: string ,
41
42
} ;
42
43
44
+ const WARNING_ID_NOT_IN_HOLDER = 'not in holder' ;
45
+
43
46
type EventExtraState = {
44
47
eventId ?: string ,
45
48
params ?: Parameter [ ] ,
@@ -50,6 +53,14 @@ export type EventBlock = Blockly.Block & EventMixin & Blockly.BlockSvg;
50
53
interface EventMixin extends EventMixinType {
51
54
mrcEventId : string ,
52
55
mrcParameters : Parameter [ ] ,
56
+
57
+ /**
58
+ * mrcHasWarning is set to true if we set the warning text on the block. It is checked to avoid
59
+ * adding a warning if there already is one. Otherwise, if we get two move events (one for drag
60
+ * and one for snap), and we call setWarningText for both events, we get a detached warning
61
+ * balloon. See https://github.com/wpilibsuite/systemcore-blocks-interface/issues/248.
62
+ */
63
+ mrcHasWarning : boolean ,
53
64
}
54
65
type EventMixinType = typeof EVENT ;
55
66
@@ -59,12 +70,11 @@ const EVENT = {
59
70
*/
60
71
init : function ( this : EventBlock ) : void {
61
72
this . setStyle ( MRC_STYLE_EVENTS ) ;
62
- this . appendDummyInput ( "TITLE" )
73
+ this . appendDummyInput ( INPUT_TITLE )
63
74
. appendField ( new Blockly . FieldTextInput ( 'my_event' ) , FIELD_EVENT_NAME ) ;
64
75
this . setPreviousStatement ( true , OUTPUT_NAME ) ;
65
76
this . setNextStatement ( true , OUTPUT_NAME ) ;
66
77
this . setMutator ( new Blockly . icons . MutatorIcon ( [ MUTATOR_BLOCK_NAME ] , this ) ) ;
67
- ChangeFramework . registerCallback ( BLOCK_NAME , [ Blockly . Events . BLOCK_MOVE ] , this . onBlockChanged ) ;
68
78
} ,
69
79
70
80
/**
@@ -91,6 +101,7 @@ const EVENT = {
91
101
loadExtraState : function ( this : EventBlock , extraState : EventExtraState ) : void {
92
102
this . mrcEventId = extraState . eventId ? extraState . eventId : this . id ;
93
103
this . mrcParameters = [ ] ;
104
+ this . mrcHasWarning = false ;
94
105
95
106
if ( extraState . params ) {
96
107
extraState . params . forEach ( ( arg ) => {
@@ -108,7 +119,7 @@ const EVENT = {
108
119
*/
109
120
updateBlock_ : function ( this : EventBlock ) : void {
110
121
const name = this . getFieldValue ( FIELD_EVENT_NAME ) ;
111
- const input = this . getInput ( 'TITLE' ) ;
122
+ const input = this . getInput ( INPUT_TITLE ) ;
112
123
if ( ! input ) {
113
124
return ;
114
125
}
@@ -136,8 +147,7 @@ const EVENT = {
136
147
paramBlock . originalName = param . name ;
137
148
}
138
149
this . mrcParameters . push ( param ) ;
139
- paramBlock =
140
- paramBlock . nextConnection && paramBlock . nextConnection . targetBlock ( ) ;
150
+ paramBlock = paramBlock . nextConnection && paramBlock . nextConnection . targetBlock ( ) ;
141
151
}
142
152
this . mrcUpdateParams ( ) ;
143
153
mutateMethodCallers ( this . workspace , this . mrcEventId , this . getEvent ( ) ) ;
@@ -152,7 +162,7 @@ const EVENT = {
152
162
let connection = topBlock ! . getInput ( 'STACK' ) ! . connection ;
153
163
154
164
for ( let i = 0 ; i < this . mrcParameters . length ; i ++ ) {
155
- let itemBlock = workspace . newBlock ( MUTATOR_BLOCK_NAME ) ;
165
+ const itemBlock = workspace . newBlock ( MUTATOR_BLOCK_NAME ) ;
156
166
( itemBlock as Blockly . BlockSvg ) . initSvg ( ) ;
157
167
itemBlock . setFieldValue ( this . mrcParameters [ i ] . name , 'NAME' ) ;
158
168
( itemBlock as MethodMutatorArgBlock ) . originalName = this . mrcParameters [ i ] . name ;
@@ -164,11 +174,11 @@ const EVENT = {
164
174
} ,
165
175
mrcUpdateParams : function ( this : EventBlock ) {
166
176
if ( this . mrcParameters . length > 0 ) {
167
- let input = this . getInput ( 'TITLE' ) ;
177
+ const input = this . getInput ( INPUT_TITLE ) ;
168
178
if ( input ) {
169
179
this . removeParameterFields ( input ) ;
170
180
this . mrcParameters . forEach ( ( param ) => {
171
- const paramName = 'PARAM_' + param . name ;
181
+ const paramName = FIELD_PARAM_PREFIX + param . name ;
172
182
const field = new Blockly . FieldTextInput ( param . name ) ;
173
183
field . EDITABLE = false ;
174
184
input . appendField ( field , paramName ) ;
@@ -178,7 +188,7 @@ const EVENT = {
178
188
} ,
179
189
removeParameterFields : function ( input : Blockly . Input ) {
180
190
const fieldsToRemove = input . fieldRow
181
- . filter ( field => field . name ?. startsWith ( 'PARAM_' ) )
191
+ . filter ( field => field . name ?. startsWith ( FIELD_PARAM_PREFIX ) )
182
192
. map ( field => field . name ! ) ;
183
193
184
194
fieldsToRemove . forEach ( fieldName => {
@@ -197,21 +207,33 @@ const EVENT = {
197
207
}
198
208
return legalName ;
199
209
} ,
200
- onBlockChanged ( block : Blockly . BlockSvg , blockEvent : Blockly . Events . BlockBase ) : void {
201
- const blockBlock = block as Blockly . Block ;
202
-
203
- if ( blockEvent . type === Blockly . Events . BLOCK_MOVE ) {
204
- const parent = ChangeFramework . getParentOfType ( block , MRC_MECHANISM_COMPONENT_HOLDER ) ;
205
-
206
- if ( parent ) {
207
- // If it is, we allow it to stay.
208
- blockBlock . setWarningText ( null ) ;
209
- return ;
210
+ /**
211
+ * mrcOnLoad is called for each EventBlock when the blocks are loaded in the blockly workspace.
212
+ */
213
+ mrcOnLoad : function ( this : EventBlock ) : void {
214
+ this . checkParentIsHolder ( ) ;
215
+ } ,
216
+ /**
217
+ * mrcOnLoad is called when an EventBlock is moved.
218
+ */
219
+ mrcOnMove : function ( this : EventBlock ) : void {
220
+ this . checkParentIsHolder ( ) ;
221
+ } ,
222
+ checkParentIsHolder : function ( this : EventBlock ) : void {
223
+ const parentBlock = this . getParent ( ) ;
224
+ if ( parentBlock && parentBlock . type === MRC_MECHANISM_COMPONENT_HOLDER ) {
225
+ // If the parent block is the mechanism_component_holder, the event block is allowed to stay.
226
+ // Remove any previous warning.
227
+ this . setWarningText ( null , WARNING_ID_NOT_IN_HOLDER ) ;
228
+ this . mrcHasWarning = false ;
229
+ } else {
230
+ // Otherwise, add a warning to the block.
231
+ this . unplug ( true ) ;
232
+ if ( ! this . mrcHasWarning ) {
233
+ this . setWarningText ( Blockly . Msg . WARNING_EVENT_NOT_IN_HOLDER , WARNING_ID_NOT_IN_HOLDER ) ;
234
+ this . getIcon ( Blockly . icons . IconType . WARNING ) ! . setBubbleVisible ( true ) ;
235
+ this . mrcHasWarning = true ;
210
236
}
211
- // If we end up here it shouldn't be allowed
212
- block . unplug ( true ) ;
213
- blockBlock . setWarningText ( 'Events can only go in the events section of the robot or mechanism' ) ;
214
- blockBlock . getIcon ( Blockly . icons . IconType . WARNING ) ! . setBubbleVisible ( true ) ;
215
237
}
216
238
} ,
217
239
getEvent : function ( this : EventBlock ) : storageModuleContent . Event {
0 commit comments