scratch-blocks 2.1.4 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +76 -24
- package/README.md +40 -0
- package/dist/main.mjs +1 -1
- package/dist/types/src/block_reporting.d.ts.map +1 -1
- package/dist/types/src/blocks/procedures.d.ts +2 -2
- package/dist/types/src/blocks/procedures.d.ts.map +1 -1
- package/dist/types/src/checkable_continuous_flyout.d.ts +6 -3
- package/dist/types/src/checkable_continuous_flyout.d.ts.map +1 -1
- package/dist/types/src/checkbox_bubble.d.ts +7 -7
- package/dist/types/src/checkbox_bubble.d.ts.map +1 -1
- package/dist/types/src/colours.d.ts.map +1 -1
- package/dist/types/src/context_menu_items.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_base.d.ts +1 -1
- package/dist/types/src/events/events_block_comment_base.d.ts.map +1 -1
- package/dist/types/src/events/events_block_drag_end.d.ts +1 -1
- package/dist/types/src/events/events_block_drag_end.d.ts.map +1 -1
- package/dist/types/src/events/events_block_drag_outside.d.ts +1 -1
- package/dist/types/src/events/events_block_drag_outside.d.ts.map +1 -1
- package/dist/types/src/fields/field_colour_slider.d.ts.map +1 -1
- package/dist/types/src/fields/field_matrix.d.ts.map +1 -1
- package/dist/types/src/fields/field_note.d.ts +6 -4
- package/dist/types/src/fields/field_note.d.ts.map +1 -1
- package/dist/types/src/fields/field_textinput_removable.d.ts.map +1 -1
- package/dist/types/src/fields/field_variable_getter.d.ts.map +1 -1
- package/dist/types/src/fields/field_vertical_separator.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_angle.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_dropdown.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_number.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_variable.d.ts +1 -0
- package/dist/types/src/fields/scratch_field_variable.d.ts.map +1 -1
- package/dist/types/src/flyout_checkbox_icon.d.ts +5 -5
- package/dist/types/src/flyout_checkbox_icon.d.ts.map +1 -1
- package/dist/types/src/glows.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/procedures.d.ts +4 -4
- package/dist/types/src/procedures.d.ts.map +1 -1
- package/dist/types/src/recyclable_block_flyout_inflater.d.ts +2 -2
- package/dist/types/src/recyclable_block_flyout_inflater.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/cat_face.d.ts +1 -1
- package/dist/types/src/renderer/cat/cat_face.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/drawer.d.ts.map +1 -1
- package/dist/types/src/renderer/constants.d.ts.map +1 -1
- package/dist/types/src/renderer/drawer.d.ts.map +1 -1
- package/dist/types/src/renderer/render_info.d.ts.map +1 -1
- package/dist/types/src/scratch_blocks_utils.d.ts +22 -0
- package/dist/types/src/scratch_blocks_utils.d.ts.map +1 -1
- package/dist/types/src/scratch_c_block_wrap.d.ts +2 -0
- package/dist/types/src/scratch_c_block_wrap.d.ts.map +1 -0
- package/dist/types/src/scratch_comment_bubble.d.ts +4 -4
- package/dist/types/src/scratch_comment_bubble.d.ts.map +1 -1
- package/dist/types/src/scratch_comment_icon.d.ts +1 -1
- package/dist/types/src/scratch_comment_icon.d.ts.map +1 -1
- package/dist/types/src/scratch_continuous_category.d.ts +3 -1
- package/dist/types/src/scratch_continuous_category.d.ts.map +1 -1
- package/dist/types/src/scratch_continuous_toolbox.d.ts +2 -1
- package/dist/types/src/scratch_continuous_toolbox.d.ts.map +1 -1
- package/dist/types/src/status_indicator_label.d.ts +3 -3
- package/dist/types/src/status_indicator_label.d.ts.map +1 -1
- package/dist/types/src/status_indicator_label_flyout_inflater.d.ts.map +1 -1
- package/dist/types/src/variables.d.ts +1 -1
- package/dist/types/src/variables.d.ts.map +1 -1
- package/dist/types/src/workspace_block_lookup.d.ts +4 -0
- package/dist/types/src/workspace_block_lookup.d.ts.map +1 -0
- package/eslint.config.mjs +23 -26
- package/package.json +10 -3
- package/src/block_reporting.ts +5 -5
- package/src/blocks/control.ts +5 -5
- package/src/blocks/event.ts +1 -1
- package/src/blocks/motion.ts +2 -2
- package/src/blocks/procedures.ts +162 -69
- package/src/blocks/sensing.ts +0 -1
- package/src/blocks/vertical_extensions.ts +11 -8
- package/src/checkable_continuous_flyout.ts +45 -12
- package/src/checkbox_bubble.ts +7 -7
- package/src/colours.ts +4 -2
- package/src/context_menu_items.ts +41 -16
- package/src/data_category.ts +11 -3
- package/src/events/events_block_comment_base.ts +5 -1
- package/src/events/events_block_comment_change.ts +5 -1
- package/src/events/events_block_comment_collapse.ts +6 -2
- package/src/events/events_block_comment_create.ts +5 -1
- package/src/events/events_block_comment_move.ts +6 -2
- package/src/events/events_block_comment_resize.ts +6 -2
- package/src/events/events_block_drag_end.ts +5 -1
- package/src/events/events_block_drag_outside.ts +5 -1
- package/src/events/events_scratch_variable_create.ts +5 -1
- package/src/fields/field_colour_slider.ts +3 -5
- package/src/fields/field_matrix.ts +33 -17
- package/src/fields/field_note.ts +56 -20
- package/src/fields/field_textinput_removable.ts +13 -4
- package/src/fields/field_variable_getter.ts +20 -6
- package/src/fields/field_vertical_separator.ts +5 -1
- package/src/fields/scratch_field_angle.ts +32 -21
- package/src/fields/scratch_field_dropdown.ts +6 -2
- package/src/fields/scratch_field_number.ts +22 -13
- package/src/fields/scratch_field_variable.ts +26 -12
- package/src/flyout_checkbox_icon.ts +9 -5
- package/src/glows.ts +5 -5
- package/src/index.ts +21 -11
- package/src/procedures.ts +92 -42
- package/src/recyclable_block_flyout_inflater.ts +5 -4
- package/src/renderer/cat/cat_face.ts +1 -1
- package/src/renderer/cat/drawer.ts +4 -1
- package/src/renderer/constants.ts +19 -14
- package/src/renderer/drawer.ts +2 -1
- package/src/renderer/render_info.ts +12 -9
- package/src/renderer/renderer.ts +1 -1
- package/src/scratch_blocks_utils.ts +0 -2
- package/src/scratch_c_block_wrap.ts +108 -0
- package/src/scratch_comment_bubble.ts +30 -19
- package/src/scratch_comment_icon.ts +9 -12
- package/src/scratch_connection_checker.ts +1 -2
- package/src/scratch_continuous_category.ts +20 -11
- package/src/scratch_continuous_toolbox.ts +12 -3
- package/src/scratch_dragger.ts +2 -2
- package/src/scratch_variable_map.ts +1 -1
- package/src/status_indicator_label.ts +13 -9
- package/src/status_indicator_label_flyout_inflater.ts +2 -1
- package/src/variables.ts +21 -14
- package/src/workspace_block_lookup.ts +14 -0
- package/src/xml.ts +1 -1
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +35 -0
- package/dist/types/tests/blocks/logic_ternary_test.d.ts +0 -13
- package/dist/types/tests/blocks/logic_ternary_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/block_test.d.ts +0 -4
- package/dist/types/tests/jsunit/block_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/connection_db_test.d.ts +0 -25
- package/dist/types/tests/jsunit/connection_db_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/connection_test.d.ts +0 -39
- package/dist/types/tests/jsunit/connection_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/db_test.d.ts +0 -7
- package/dist/types/tests/jsunit/db_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/event_test.d.ts +0 -76
- package/dist/types/tests/jsunit/event_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/extensions_test.d.ts +0 -18
- package/dist/types/tests/jsunit/extensions_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/field_angle_test.d.ts +0 -3
- package/dist/types/tests/jsunit/field_angle_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/field_number_test.d.ts +0 -3
- package/dist/types/tests/jsunit/field_number_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/field_test.d.ts +0 -8
- package/dist/types/tests/jsunit/field_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/field_variable_getter_test.d.ts +0 -5
- package/dist/types/tests/jsunit/field_variable_getter_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/field_variable_test.d.ts +0 -19
- package/dist/types/tests/jsunit/field_variable_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/generator_test.d.ts +0 -2
- package/dist/types/tests/jsunit/generator_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/gesture_test.d.ts +0 -10
- package/dist/types/tests/jsunit/gesture_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/input_test.d.ts +0 -9
- package/dist/types/tests/jsunit/input_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/json_test.d.ts +0 -11
- package/dist/types/tests/jsunit/json_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/names_test.d.ts +0 -5
- package/dist/types/tests/jsunit/names_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/procedure_test.d.ts +0 -15
- package/dist/types/tests/jsunit/procedure_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/scratch_block_comment_test.d.ts +0 -14
- package/dist/types/tests/jsunit/scratch_block_comment_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/svg_test.d.ts +0 -14
- package/dist/types/tests/jsunit/svg_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/test_runner.d.ts +0 -2
- package/dist/types/tests/jsunit/test_runner.d.ts.map +0 -1
- package/dist/types/tests/jsunit/test_utilities.d.ts +0 -50
- package/dist/types/tests/jsunit/test_utilities.d.ts.map +0 -1
- package/dist/types/tests/jsunit/utils_test.d.ts +0 -10
- package/dist/types/tests/jsunit/utils_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/variable_map_test.d.ts +0 -28
- package/dist/types/tests/jsunit/variable_map_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/variable_model_test.d.ts +0 -14
- package/dist/types/tests/jsunit/variable_model_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/widget_div_test.d.ts +0 -37
- package/dist/types/tests/jsunit/widget_div_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/workspace_comment_test.d.ts +0 -13
- package/dist/types/tests/jsunit/workspace_comment_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/workspace_test.d.ts +0 -22
- package/dist/types/tests/jsunit/workspace_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/workspace_undo_redo_test.d.ts +0 -33
- package/dist/types/tests/jsunit/workspace_undo_redo_test.d.ts.map +0 -1
- package/dist/types/tests/jsunit/xml_test.d.ts +0 -55
- package/dist/types/tests/jsunit/xml_test.d.ts.map +0 -1
- package/dist/types/tests/workspace_svg/workspace_svg_test.d.ts +0 -12
- package/dist/types/tests/workspace_svg/workspace_svg_test.d.ts.map +0 -1
- package/types/continuous-toolbox.d.ts +0 -1
|
@@ -35,19 +35,28 @@ export class FieldTextInputRemovable extends Blockly.FieldTextInput {
|
|
|
35
35
|
showEditor_() {
|
|
36
36
|
// Wait for our parent block to render so we can examine its metrics to
|
|
37
37
|
// calculate rounded corners on the editor as needed.
|
|
38
|
-
Blockly.renderManagement.finishQueuedRenders().then(() => {
|
|
38
|
+
void Blockly.renderManagement.finishQueuedRenders().then(() => {
|
|
39
39
|
super.showEditor_()
|
|
40
40
|
|
|
41
|
-
const div = Blockly.WidgetDiv.getDiv()
|
|
41
|
+
const div = Blockly.WidgetDiv.getDiv()
|
|
42
|
+
if (!div) {
|
|
43
|
+
console.error('[field_textinput_removable] Missing WidgetDiv for removable text input')
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
if (!this.sourceBlock_) {
|
|
47
|
+
console.error('[field_textinput_removable] Missing source block for removable text input')
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
div.className += ' removableTextInput'
|
|
43
52
|
const removeButton = document.createElement('img')
|
|
44
53
|
removeButton.className = 'blocklyTextRemoveIcon'
|
|
45
|
-
removeButton.setAttribute('src', this.sourceBlock_
|
|
54
|
+
removeButton.setAttribute('src', this.sourceBlock_.workspace.options.pathToMedia + 'icons/remove.svg')
|
|
46
55
|
this.removeButtonMouseWrapper_ = Blockly.browserEvents.bind(
|
|
47
56
|
removeButton,
|
|
48
57
|
'mousedown',
|
|
49
58
|
this,
|
|
50
|
-
this.removeCallback_,
|
|
59
|
+
this.removeCallback_.bind(this),
|
|
51
60
|
)
|
|
52
61
|
div.appendChild(removeButton)
|
|
53
62
|
})
|
|
@@ -70,8 +70,13 @@ class FieldVariableGetter extends Blockly.FieldLabel {
|
|
|
70
70
|
*/
|
|
71
71
|
doValueUpdate_(newVariableId: string) {
|
|
72
72
|
super.doValueUpdate_(newVariableId)
|
|
73
|
-
const
|
|
74
|
-
|
|
73
|
+
const sourceBlock = this.getSourceBlock()
|
|
74
|
+
if (!sourceBlock) {
|
|
75
|
+
console.error('[field_variable_getter] Missing source block in doValueUpdate_')
|
|
76
|
+
this.variable = null
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
this.variable = Blockly.Variables.getVariable(sourceBlock.workspace, newVariableId)
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
/**
|
|
@@ -92,13 +97,22 @@ class FieldVariableGetter extends Blockly.FieldLabel {
|
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
fromXml(element: Element) {
|
|
95
|
-
|
|
100
|
+
const id = element.getAttribute('id')
|
|
101
|
+
if (!id) {
|
|
102
|
+
console.error('[field_variable_getter] Missing variable id in XML')
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
this.setValue(id)
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
toXml(element: Element): Element {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
if (!this.variable) {
|
|
110
|
+
console.error('[field_variable_getter] Missing variable in toXml')
|
|
111
|
+
return element
|
|
112
|
+
}
|
|
113
|
+
element.setAttribute('id', this.variable.getId())
|
|
114
|
+
element.setAttribute('variabletype', this.variable.getType())
|
|
115
|
+
element.textContent = this.variable.getName()
|
|
102
116
|
return element
|
|
103
117
|
}
|
|
104
118
|
}
|
|
@@ -73,7 +73,11 @@ class FieldVerticalSeparator extends Blockly.Field {
|
|
|
73
73
|
* @package
|
|
74
74
|
*/
|
|
75
75
|
setLineHeight(newHeight: number) {
|
|
76
|
-
this.lineElement
|
|
76
|
+
if (!this.lineElement) {
|
|
77
|
+
console.error('[field_vertical_separator] Missing lineElement in setLineHeight')
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
this.lineElement.setAttribute('y2', `${newHeight}`)
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
/**
|
|
@@ -47,17 +47,17 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
47
47
|
/**
|
|
48
48
|
* Opaque identifier used to unbind event listener in dispose().
|
|
49
49
|
*/
|
|
50
|
-
private mouseDownWrapper_
|
|
50
|
+
private mouseDownWrapper_?: Blockly.browserEvents.Data
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Opaque identifier used to unbind event listener in dispose().
|
|
54
54
|
*/
|
|
55
|
-
private mouseMoveWrapper
|
|
55
|
+
private mouseMoveWrapper?: Blockly.browserEvents.Data
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Opaque identifier used to unbind event listener in dispose().
|
|
59
59
|
*/
|
|
60
|
-
private mouseUpWrapper
|
|
60
|
+
private mouseUpWrapper?: Blockly.browserEvents.Data
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Round angles to the nearest 15 degrees when using mouse.
|
|
@@ -164,6 +164,14 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
164
164
|
Blockly.DropDownDiv.hideWithoutAnimation()
|
|
165
165
|
Blockly.DropDownDiv.clearContent()
|
|
166
166
|
const div = Blockly.DropDownDiv.getContentDiv()
|
|
167
|
+
const sourceBlock = this.getSourceBlock()
|
|
168
|
+
if (!(sourceBlock instanceof Blockly.BlockSvg)) {
|
|
169
|
+
throw new Error('[scratch_field_angle] Missing source BlockSvg for showEditor_')
|
|
170
|
+
}
|
|
171
|
+
const parentBlock = sourceBlock.getParent()
|
|
172
|
+
if (!parentBlock) {
|
|
173
|
+
throw new Error('[scratch_field_angle] Missing parent block for showEditor_')
|
|
174
|
+
}
|
|
167
175
|
// Build the SVG DOM.
|
|
168
176
|
const svg = Blockly.utils.dom.createSvgElement(
|
|
169
177
|
'svg',
|
|
@@ -183,8 +191,8 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
183
191
|
cx: this.HALF,
|
|
184
192
|
cy: this.HALF,
|
|
185
193
|
r: this.RADIUS,
|
|
186
|
-
fill:
|
|
187
|
-
stroke:
|
|
194
|
+
fill: parentBlock.getColourSecondary(),
|
|
195
|
+
stroke: parentBlock.getColourTertiary(),
|
|
188
196
|
class: 'blocklyAngleCircle',
|
|
189
197
|
},
|
|
190
198
|
svg,
|
|
@@ -268,13 +276,10 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
268
276
|
Blockly.getMainWorkspace().options.pathToMedia + this.ARROW_SVG_PATH,
|
|
269
277
|
)
|
|
270
278
|
|
|
271
|
-
Blockly.DropDownDiv.setColour(
|
|
272
|
-
|
|
273
|
-
(this.getSourceBlock()!.getParent() as Blockly.BlockSvg).getColourTertiary(),
|
|
274
|
-
)
|
|
275
|
-
Blockly.DropDownDiv.showPositionedByBlock(this, this.getSourceBlock() as Blockly.BlockSvg)
|
|
279
|
+
Blockly.DropDownDiv.setColour(parentBlock.getColour(), parentBlock.getColourTertiary())
|
|
280
|
+
Blockly.DropDownDiv.showPositionedByBlock(this as Blockly.Field<string | number | null>, sourceBlock)
|
|
276
281
|
|
|
277
|
-
this.mouseDownWrapper_ = Blockly.browserEvents.bind(this.handle, 'mousedown', this, this.onMouseDown)
|
|
282
|
+
this.mouseDownWrapper_ = Blockly.browserEvents.bind(this.handle, 'mousedown', this, this.onMouseDown.bind(this))
|
|
278
283
|
|
|
279
284
|
this.updateGraph()
|
|
280
285
|
}
|
|
@@ -283,16 +288,20 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
283
288
|
* Set the angle to match the mouse's position.
|
|
284
289
|
*/
|
|
285
290
|
onMouseDown() {
|
|
286
|
-
this.mouseMoveWrapper = Blockly.browserEvents.bind(document.body, 'mousemove', this, this.onMouseMove)
|
|
287
|
-
this.mouseUpWrapper = Blockly.browserEvents.bind(document.body, 'mouseup', this, this.onMouseUp)
|
|
291
|
+
this.mouseMoveWrapper = Blockly.browserEvents.bind(document.body, 'mousemove', this, this.onMouseMove.bind(this))
|
|
292
|
+
this.mouseUpWrapper = Blockly.browserEvents.bind(document.body, 'mouseup', this, this.onMouseUp.bind(this))
|
|
288
293
|
}
|
|
289
294
|
|
|
290
295
|
/**
|
|
291
296
|
* Set the angle to match the mouse's position.
|
|
292
297
|
*/
|
|
293
298
|
onMouseUp() {
|
|
294
|
-
|
|
295
|
-
|
|
299
|
+
if (this.mouseMoveWrapper) {
|
|
300
|
+
Blockly.browserEvents.unbind(this.mouseMoveWrapper)
|
|
301
|
+
}
|
|
302
|
+
if (this.mouseUpWrapper) {
|
|
303
|
+
Blockly.browserEvents.unbind(this.mouseUpWrapper)
|
|
304
|
+
}
|
|
296
305
|
}
|
|
297
306
|
|
|
298
307
|
/**
|
|
@@ -301,7 +310,9 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
301
310
|
*/
|
|
302
311
|
onMouseMove(e: PointerEvent) {
|
|
303
312
|
e.preventDefault()
|
|
304
|
-
const
|
|
313
|
+
const ownerSvg = this.gauge?.ownerSVGElement
|
|
314
|
+
if (!ownerSvg) return
|
|
315
|
+
const bBox = ownerSvg.getBoundingClientRect()
|
|
305
316
|
const dx = e.clientX - bBox.left - this.HALF
|
|
306
317
|
const dy = e.clientY - bBox.top - this.HALF
|
|
307
318
|
let angle = Math.atan(-dy / dx)
|
|
@@ -383,12 +394,12 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
383
394
|
} else {
|
|
384
395
|
imageRotation = -angleDegrees
|
|
385
396
|
}
|
|
386
|
-
this.arrow
|
|
397
|
+
this.arrow?.setAttribute('transform', 'rotate(' + imageRotation + ')')
|
|
387
398
|
}
|
|
388
399
|
this.gauge.setAttribute('d', path.join(''))
|
|
389
|
-
this.line
|
|
390
|
-
this.line
|
|
391
|
-
this.handle
|
|
400
|
+
this.line?.setAttribute('x2', `${x2}`)
|
|
401
|
+
this.line?.setAttribute('y2', `${y2}`)
|
|
402
|
+
this.handle?.setAttribute('transform', 'translate(' + x2 + ',' + y2 + ')')
|
|
392
403
|
}
|
|
393
404
|
|
|
394
405
|
/**
|
|
@@ -396,7 +407,7 @@ class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
|
396
407
|
* @param text The user's text.
|
|
397
408
|
* @returns A string representing a valid angle, or null if invalid.
|
|
398
409
|
*/
|
|
399
|
-
doClassValidation_(text: string): number | null {
|
|
410
|
+
doClassValidation_(text: string | null): number | null {
|
|
400
411
|
if (text === null) {
|
|
401
412
|
return null
|
|
402
413
|
}
|
|
@@ -17,14 +17,18 @@ class ScratchFieldDropdown extends Blockly.FieldDropdown {
|
|
|
17
17
|
} else if (this.borderRect_) {
|
|
18
18
|
this.borderRect_.setAttribute(
|
|
19
19
|
'fill',
|
|
20
|
-
'colourQuaternary' in style ?
|
|
20
|
+
'colourQuaternary' in style ? String(style.colourQuaternary) : style.colourTertiary,
|
|
21
21
|
)
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
dropdownDispose_() {
|
|
26
26
|
super.dropdownDispose_()
|
|
27
|
-
const sourceBlock = this.getSourceBlock()
|
|
27
|
+
const sourceBlock = this.getSourceBlock()
|
|
28
|
+
if (!sourceBlock) {
|
|
29
|
+
console.error('[scratch_field_dropdown] Missing source block in dropdownDispose_')
|
|
30
|
+
return
|
|
31
|
+
}
|
|
28
32
|
if (sourceBlock.isShadow()) {
|
|
29
33
|
sourceBlock.setStyle(this.originalStyle)
|
|
30
34
|
}
|
|
@@ -110,14 +110,14 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
|
|
|
110
110
|
* appropriate.
|
|
111
111
|
* @param e The triggering pointer event.
|
|
112
112
|
*/
|
|
113
|
-
showEditor_(e
|
|
113
|
+
showEditor_(e?: PointerEvent) {
|
|
114
114
|
// Do not focus on mobile devices so we can show the num-pad
|
|
115
115
|
const showNumPad = e?.pointerType === 'touch'
|
|
116
116
|
super.showEditor_(e, showNumPad)
|
|
117
117
|
|
|
118
118
|
// Show a numeric keypad in the drop-down on touch
|
|
119
119
|
if (showNumPad) {
|
|
120
|
-
this.htmlInput_
|
|
120
|
+
this.htmlInput_?.select()
|
|
121
121
|
this.showNumPad_()
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -148,7 +148,10 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
|
|
|
148
148
|
|
|
149
149
|
// Set colour and size of drop-down
|
|
150
150
|
const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg
|
|
151
|
-
|
|
151
|
+
const parentBlock = sourceBlock.getParent()
|
|
152
|
+
if (parentBlock) {
|
|
153
|
+
Blockly.DropDownDiv.setColour(parentBlock.getColour(), sourceBlock.getColourTertiary())
|
|
154
|
+
}
|
|
152
155
|
contentDiv.style.width = ScratchFieldNumber.DROPDOWN_WIDTH + 'px'
|
|
153
156
|
|
|
154
157
|
this.position_()
|
|
@@ -176,7 +179,7 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
|
|
|
176
179
|
Blockly.DropDownDiv.setBoundsElement(sourceBlock.workspace.getParentSvg().parentElement)
|
|
177
180
|
Blockly.DropDownDiv.show(
|
|
178
181
|
this,
|
|
179
|
-
|
|
182
|
+
sourceBlock.RTL,
|
|
180
183
|
primaryX,
|
|
181
184
|
primaryY,
|
|
182
185
|
secondaryX,
|
|
@@ -193,8 +196,9 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
|
|
|
193
196
|
*/
|
|
194
197
|
private addButtons_(contentDiv: Element) {
|
|
195
198
|
const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg
|
|
196
|
-
const
|
|
197
|
-
const
|
|
199
|
+
const parent = sourceBlock.getParent()
|
|
200
|
+
const buttonColour = parent?.getColour() ?? sourceBlock.getColour()
|
|
201
|
+
const buttonBorderColour = parent?.getColourTertiary() ?? sourceBlock.getColourTertiary()
|
|
198
202
|
|
|
199
203
|
// Add numeric keypad buttons
|
|
200
204
|
const buttons = ScratchFieldNumber.NUMPAD_BUTTONS
|
|
@@ -248,10 +252,12 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
|
|
|
248
252
|
// String of the button (e.g., '7')
|
|
249
253
|
const spliceValue = (e.target as HTMLElement).innerText
|
|
250
254
|
// Old value of the text field
|
|
251
|
-
const
|
|
255
|
+
const htmlInput = this.htmlInput_
|
|
256
|
+
if (!htmlInput) return
|
|
257
|
+
const oldValue = htmlInput.value
|
|
252
258
|
// Determine the selected portion of the text field
|
|
253
|
-
const selectionStart =
|
|
254
|
-
const selectionEnd =
|
|
259
|
+
const selectionStart = htmlInput.selectionStart ?? 0
|
|
260
|
+
const selectionEnd = htmlInput.selectionEnd ?? 0
|
|
255
261
|
|
|
256
262
|
// Splice in the new value
|
|
257
263
|
const newValue = oldValue.slice(0, selectionStart) + spliceValue + oldValue.slice(selectionEnd)
|
|
@@ -273,10 +279,12 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
|
|
|
273
279
|
*/
|
|
274
280
|
numPadEraseButtonTouch(e: PointerEvent) {
|
|
275
281
|
// Old value of the text field
|
|
276
|
-
const
|
|
282
|
+
const htmlInput = this.htmlInput_
|
|
283
|
+
if (!htmlInput) return
|
|
284
|
+
const oldValue = htmlInput.value
|
|
277
285
|
// Determine what is selected to erase (if anything)
|
|
278
|
-
let selectionStart =
|
|
279
|
-
const selectionEnd =
|
|
286
|
+
let selectionStart = htmlInput.selectionStart ?? 0
|
|
287
|
+
const selectionEnd = htmlInput.selectionEnd ?? 0
|
|
280
288
|
|
|
281
289
|
// If selection is zero-length, shift start to the left 1 character
|
|
282
290
|
if (selectionStart == selectionEnd) {
|
|
@@ -303,7 +311,8 @@ class ScratchFieldNumber extends Blockly.FieldTextInput {
|
|
|
303
311
|
private updateDisplay_(newValue: string, newSelection: number) {
|
|
304
312
|
this.setEditorValue_(newValue)
|
|
305
313
|
// Resize and scroll the text field appropriately
|
|
306
|
-
const htmlInput = this.htmlInput_
|
|
314
|
+
const htmlInput = this.htmlInput_
|
|
315
|
+
if (!htmlInput) return
|
|
307
316
|
htmlInput.setSelectionRange(newSelection, newSelection)
|
|
308
317
|
htmlInput.scrollLeft = htmlInput.scrollWidth
|
|
309
318
|
}
|
|
@@ -29,6 +29,13 @@ import { createVariable, renameVariable } from '../variables'
|
|
|
29
29
|
export class ScratchFieldVariable extends Blockly.FieldVariable {
|
|
30
30
|
private originalStyle!: string
|
|
31
31
|
|
|
32
|
+
private getSourceWorkspaceSvg_(sourceBlock: Blockly.Block): Blockly.WorkspaceSvg {
|
|
33
|
+
if (!(sourceBlock.workspace instanceof Blockly.WorkspaceSvg)) {
|
|
34
|
+
throw new Error('[scratch_field_variable] Expected source block workspace to be a WorkspaceSvg')
|
|
35
|
+
}
|
|
36
|
+
return sourceBlock.workspace
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
constructor(
|
|
33
40
|
varName: string | null | typeof Blockly.Field.SKIP_SETUP,
|
|
34
41
|
validator?: Blockly.FieldVariableValidator,
|
|
@@ -40,14 +47,15 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
|
|
|
40
47
|
// dropdownCreate returns MenuOption[] rather than Blockly.MenuGenerator's
|
|
41
48
|
// MenuOption[][] variant; the cast is needed to satisfy FieldVariable's
|
|
42
49
|
// menuGenerator_ type while the actual runtime shape is compatible.
|
|
43
|
-
this.menuGenerator_ = ScratchFieldVariable.dropdownCreate as unknown as Blockly.MenuGenerator
|
|
50
|
+
this.menuGenerator_ = ScratchFieldVariable.dropdownCreate.bind(this) as unknown as Blockly.MenuGenerator
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
initModel() {
|
|
47
54
|
if (!this.getVariable()) {
|
|
48
55
|
const sourceBlock = this.getSourceBlock()
|
|
49
56
|
if (sourceBlock) {
|
|
50
|
-
const
|
|
57
|
+
const sourceWorkspace = this.getSourceWorkspaceSvg_(sourceBlock)
|
|
58
|
+
const broadcastVariable = this.initFlyoutBroadcast(sourceWorkspace)
|
|
51
59
|
if (broadcastVariable) {
|
|
52
60
|
this.doValueUpdate_(broadcastVariable.getId())
|
|
53
61
|
return
|
|
@@ -96,7 +104,8 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
|
|
|
96
104
|
if (option[1] === Blockly.RENAME_VARIABLE_ID) {
|
|
97
105
|
return [ScratchMsgs.translate('RENAME_LIST'), option[1]]
|
|
98
106
|
} else if (option[1] === Blockly.DELETE_VARIABLE_ID) {
|
|
99
|
-
|
|
107
|
+
const fieldText = (this as Blockly.FieldVariable).getText()
|
|
108
|
+
return [String(ScratchMsgs.translate('DELETE_LIST')).replace('%1', fieldText), option[1]]
|
|
100
109
|
}
|
|
101
110
|
return option
|
|
102
111
|
})
|
|
@@ -118,8 +127,9 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
|
|
|
118
127
|
if (sourceBlock && !sourceBlock.isDeadOrDying()) {
|
|
119
128
|
const selectedItem = menuItem.getValue()
|
|
120
129
|
if (selectedItem === Constants.NEW_BROADCAST_MESSAGE_ID) {
|
|
130
|
+
const sourceWorkspace = this.getSourceWorkspaceSvg_(sourceBlock)
|
|
121
131
|
createVariable(
|
|
122
|
-
|
|
132
|
+
sourceWorkspace,
|
|
123
133
|
(varId) => {
|
|
124
134
|
if (varId) {
|
|
125
135
|
this.setValue(varId)
|
|
@@ -129,7 +139,7 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
|
|
|
129
139
|
)
|
|
130
140
|
return
|
|
131
141
|
} else if (selectedItem === Blockly.RENAME_VARIABLE_ID) {
|
|
132
|
-
renameVariable(sourceBlock.workspace
|
|
142
|
+
renameVariable(sourceBlock.workspace, this.getVariable() as ScratchVariableModel)
|
|
133
143
|
return
|
|
134
144
|
}
|
|
135
145
|
}
|
|
@@ -138,26 +148,30 @@ export class ScratchFieldVariable extends Blockly.FieldVariable {
|
|
|
138
148
|
|
|
139
149
|
showEditor_(event: PointerEvent) {
|
|
140
150
|
super.showEditor_(event)
|
|
141
|
-
const sourceBlock = this.getSourceBlock()
|
|
151
|
+
const sourceBlock = this.getSourceBlock()
|
|
152
|
+
if (!sourceBlock) {
|
|
153
|
+
throw new Error('[scratch_field_variable] Missing source block in showEditor_')
|
|
154
|
+
}
|
|
155
|
+
const sourceWorkspace = this.getSourceWorkspaceSvg_(sourceBlock)
|
|
142
156
|
const styleName = sourceBlock.getStyleName()
|
|
143
|
-
const style = (
|
|
144
|
-
.getRenderer()
|
|
145
|
-
.getConstants()
|
|
146
|
-
.getBlockStyle(styleName)
|
|
157
|
+
const style = sourceWorkspace.getRenderer().getConstants().getBlockStyle(styleName)
|
|
147
158
|
if (sourceBlock.isShadow()) {
|
|
148
159
|
this.originalStyle = styleName
|
|
149
160
|
sourceBlock.setStyle(`${this.originalStyle}_selected`)
|
|
150
161
|
} else if (this.borderRect_) {
|
|
151
162
|
this.borderRect_.setAttribute(
|
|
152
163
|
'fill',
|
|
153
|
-
'colourQuaternary' in style ?
|
|
164
|
+
'colourQuaternary' in style ? String(style.colourQuaternary) : style.colourTertiary,
|
|
154
165
|
)
|
|
155
166
|
}
|
|
156
167
|
}
|
|
157
168
|
|
|
158
169
|
dropdownDispose_() {
|
|
159
170
|
super.dropdownDispose_()
|
|
160
|
-
const sourceBlock = this.getSourceBlock()
|
|
171
|
+
const sourceBlock = this.getSourceBlock()
|
|
172
|
+
if (!sourceBlock) {
|
|
173
|
+
throw new Error('[scratch_field_variable] Missing source block in dropdownDispose_')
|
|
174
|
+
}
|
|
161
175
|
if (sourceBlock.isShadow()) {
|
|
162
176
|
sourceBlock.setStyle(this.originalStyle)
|
|
163
177
|
}
|
|
@@ -9,7 +9,7 @@ import { CheckboxBubble } from './checkbox_bubble'
|
|
|
9
9
|
* Invisible icon that exists solely to host the corresponding checkbox bubble.
|
|
10
10
|
*/
|
|
11
11
|
export class FlyoutCheckboxIcon extends Blockly.icons.Icon implements Blockly.IHasBubble {
|
|
12
|
-
private checkboxBubble
|
|
12
|
+
private checkboxBubble?: CheckboxBubble
|
|
13
13
|
private type = new Blockly.icons.IconType('checkbox')
|
|
14
14
|
|
|
15
15
|
constructor(protected override sourceBlock: Blockly.BlockSvg) {
|
|
@@ -36,7 +36,7 @@ export class FlyoutCheckboxIcon extends Blockly.icons.Icon implements Blockly.IH
|
|
|
36
36
|
return this.sourceBlock.workspace.isFlyout
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
onLocationChange(
|
|
39
|
+
onLocationChange(_blockOrigin: Blockly.utils.Coordinate) {
|
|
40
40
|
this.checkboxBubble?.updateLocation()
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -52,16 +52,20 @@ export class FlyoutCheckboxIcon extends Blockly.icons.Icon implements Blockly.IH
|
|
|
52
52
|
// These methods are required by the interfaces, but intentionally have no
|
|
53
53
|
// implementation, largely because this icon has no visual representation.
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
setBubbleVisible(_visible: boolean): Promise<void> {
|
|
56
|
+
return Promise.resolve()
|
|
57
|
+
}
|
|
56
58
|
|
|
57
|
-
initView(
|
|
59
|
+
initView(_pointerDownListener: (e: PointerEvent) => void) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
58
62
|
|
|
59
63
|
canBeFocused() {
|
|
60
64
|
return false
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
getBubble() {
|
|
64
|
-
return this.checkboxBubble
|
|
68
|
+
return this.checkboxBubble ?? null
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
|
package/src/glows.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import * as Blockly from 'blockly/core'
|
|
6
6
|
import { Colours } from './colours'
|
|
7
|
+
import { getBlockSvgById, getRequiredMainWorkspaceSvg } from './workspace_block_lookup'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Glow/unglow a stack in the workspace.
|
|
@@ -11,11 +12,10 @@ import { Colours } from './colours'
|
|
|
11
12
|
* @param isGlowingStack Whether to glow the stack.
|
|
12
13
|
*/
|
|
13
14
|
export function glowStack(id: string, isGlowingStack: boolean) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
?.getBlockById(id)) as Blockly.BlockSvg
|
|
15
|
+
const mainWorkspace = getRequiredMainWorkspaceSvg()
|
|
16
|
+
const flyout = mainWorkspace.getFlyout()
|
|
17
|
+
const flyoutBlock = flyout ? getBlockSvgById(flyout.getWorkspace(), id) : null
|
|
18
|
+
const block = getBlockSvgById(mainWorkspace, id) ?? flyoutBlock
|
|
19
19
|
if (!block) {
|
|
20
20
|
throw new Error('Tried to glow block that does not exist.')
|
|
21
21
|
}
|
package/src/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ import './renderer/cat/renderer'
|
|
|
47
47
|
import './renderer/renderer'
|
|
48
48
|
import { registerScratchBlockPaster } from './scratch_block_paster'
|
|
49
49
|
import * as scratchBlocksUtils from './scratch_blocks_utils'
|
|
50
|
+
import './scratch_c_block_wrap'
|
|
50
51
|
import './scratch_comment_icon'
|
|
51
52
|
import './scratch_connection_checker'
|
|
52
53
|
import { registerScratchContinuousCategory } from './scratch_continuous_category'
|
|
@@ -158,7 +159,7 @@ export function isContentNodeFocused(): boolean {
|
|
|
158
159
|
return Blockly.getFocusManager().getFocusedNode() !== null
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
registerContinuousToolbox()
|
|
162
|
+
;(registerContinuousToolbox as () => void)()
|
|
162
163
|
Blockly.Scrollbar.scrollbarThickness = Blockly.Touch.TOUCH_ENABLED ? 14 : 11
|
|
163
164
|
Blockly.FlyoutButton.TEXT_MARGIN_X = 40
|
|
164
165
|
Blockly.FlyoutButton.TEXT_MARGIN_Y = 10
|
|
@@ -168,12 +169,23 @@ Blockly.ContextMenuItems.registerCommentOptions()
|
|
|
168
169
|
// Blockly hides "Add Comment" for simple reporters because comments can't be
|
|
169
170
|
// read in the default renderer. In Scratch they're shown differently, so
|
|
170
171
|
// remove that restriction by dropping the isFullBlockField check.
|
|
171
|
-
Blockly.ContextMenuRegistry.registry.getItem('blockComment')
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
const blockCommentMenuItem = Blockly.ContextMenuRegistry.registry.getItem('blockComment')
|
|
173
|
+
if (!blockCommentMenuItem) {
|
|
174
|
+
console.error('[index] Missing context menu item: blockComment')
|
|
175
|
+
} else {
|
|
176
|
+
blockCommentMenuItem.preconditionFn = (scope) => {
|
|
177
|
+
const block = scope.block
|
|
178
|
+
if (
|
|
179
|
+
block &&
|
|
180
|
+
!block.isInFlyout &&
|
|
181
|
+
block.workspace.options.comments &&
|
|
182
|
+
!block.isCollapsed() &&
|
|
183
|
+
block.isEditable()
|
|
184
|
+
) {
|
|
185
|
+
return 'enabled'
|
|
186
|
+
}
|
|
187
|
+
return 'hidden'
|
|
175
188
|
}
|
|
176
|
-
return 'hidden'
|
|
177
189
|
}
|
|
178
190
|
Blockly.ContextMenuRegistry.registry.unregister('blockDelete')
|
|
179
191
|
contextMenuItems.registerDeleteBlock()
|
|
@@ -190,11 +202,9 @@ Blockly.comments.CommentView.defaultCommentSize = new Blockly.utils.Size(200, 20
|
|
|
190
202
|
// to the workspace itself (whose onNodeFocus is a no-op) rather than to a
|
|
191
203
|
// specific block, so deleting a block doesn't reset the scroll position.
|
|
192
204
|
// We may need to re-evaluate this when we explicitly work on keyboard navigation.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Blockly.WorkspaceSvg.prototype.getRestoredFocusableNode = function (
|
|
196
|
-
previousNode,
|
|
197
|
-
) {
|
|
205
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method -- preserve original prototype method for patched wrapper
|
|
206
|
+
const originalGetRestoredFocusableNode = Blockly.WorkspaceSvg.prototype.getRestoredFocusableNode
|
|
207
|
+
Blockly.WorkspaceSvg.prototype.getRestoredFocusableNode = function (previousNode) {
|
|
198
208
|
if (!previousNode && !this.isFlyout) return null
|
|
199
209
|
return originalGetRestoredFocusableNode.call(this, previousNode)
|
|
200
210
|
}
|