scratch-blocks 2.0.0-spork.2 → 2.0.0-spork.4
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/CHANGELOG.md +14 -0
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +0 -12
- package/package.json +4 -4
- package/src/{block_reporting.js → block_reporting.ts} +7 -5
- package/src/blocks/{colour.js → colour.ts} +6 -6
- package/src/blocks/{control.js → control.ts} +21 -54
- package/src/blocks/{data.js → data.ts} +134 -142
- package/src/blocks/{event.js → event.ts} +12 -33
- package/src/blocks/{looks.js → looks.ts} +24 -73
- package/src/blocks/{math.js → math.ts} +6 -11
- package/src/blocks/{matrix.js → matrix.ts} +2 -3
- package/src/blocks/{motion.js → motion.ts} +23 -70
- package/src/blocks/{note.js → note.ts} +2 -3
- package/src/blocks/{operators.js → operators.ts} +18 -55
- package/src/blocks/{procedures.js → procedures.ts} +418 -269
- package/src/blocks/{sensing.js → sensing.ts} +21 -61
- package/src/blocks/{sound.js → sound.ts} +9 -28
- package/src/blocks/{text.js → text.ts} +1 -2
- package/src/blocks/{vertical_extensions.js → vertical_extensions.ts} +63 -100
- package/src/checkable_continuous_flyout.ts +112 -0
- package/src/{checkbox_bubble.js → checkbox_bubble.ts} +40 -58
- package/src/{colours.js → colours.ts} +11 -4
- package/src/{constants.js → constants.ts} +13 -0
- package/src/{context_menu_items.js → context_menu_items.ts} +18 -12
- package/src/{css.js → css.ts} +13 -7
- package/src/{data_category.js → data_category.ts} +216 -150
- package/src/events/{events_block_comment_base.js → events_block_comment_base.ts} +23 -4
- package/src/events/{events_block_comment_change.js → events_block_comment_change.ts} +29 -5
- package/src/events/{events_block_comment_collapse.js → events_block_comment_collapse.ts} +24 -6
- package/src/events/{events_block_comment_create.js → events_block_comment_create.ts} +36 -10
- package/src/events/{events_block_comment_delete.js → events_block_comment_delete.ts} +6 -2
- package/src/events/{events_block_comment_move.js → events_block_comment_move.ts} +36 -6
- package/src/events/events_block_comment_resize.ts +88 -0
- package/src/events/events_block_drag_end.ts +49 -0
- package/src/events/events_block_drag_outside.ts +44 -0
- package/src/events/{events_scratch_variable_create.js → events_scratch_variable_create.ts} +28 -15
- package/src/fields/{field_colour_slider.js → field_colour_slider.ts} +117 -106
- package/src/fields/{field_matrix.js → field_matrix.ts} +189 -215
- package/src/fields/{field_note.js → field_note.ts} +227 -286
- package/src/fields/{field_textinput_removable.js → field_textinput_removable.ts} +17 -20
- package/src/fields/{field_variable_getter.js → field_variable_getter.ts} +28 -17
- package/src/fields/{field_vertical_separator.js → field_vertical_separator.ts} +14 -30
- package/src/fields/{field_angle.js → scratch_field_angle.ts} +124 -80
- package/src/fields/{field_dropdown.js → scratch_field_dropdown.ts} +9 -7
- package/src/fields/{field_number.js → scratch_field_number.ts} +60 -55
- package/src/fields/{field_variable.js → scratch_field_variable.ts} +46 -27
- package/src/{flyout_checkbox_icon.js → flyout_checkbox_icon.ts} +15 -19
- package/src/{glows.js → glows.ts} +29 -18
- package/src/index.ts +62 -63
- package/src/procedures.ts +462 -0
- package/src/recyclable_block_flyout_inflater.ts +51 -0
- package/src/renderer/{bowler_hat.js → bowler_hat.ts} +1 -1
- package/src/renderer/{constants.js → constants.ts} +26 -12
- package/src/renderer/{drawer.js → drawer.ts} +8 -3
- package/src/renderer/{path_object.js → path_object.ts} +2 -2
- package/src/renderer/{render_info.js → render_info.ts} +19 -7
- package/src/renderer/renderer.ts +76 -0
- package/src/{scratch_block_paster.js → scratch_block_paster.ts} +9 -7
- package/src/scratch_blocks_utils.ts +39 -0
- package/src/{scratch_comment_icon.js → scratch_comment_icon.ts} +43 -26
- package/src/scratch_connection_checker.ts +44 -0
- package/src/{scratch_continuous_category.js → scratch_continuous_category.ts} +20 -13
- package/src/scratch_continuous_toolbox.ts +70 -0
- package/src/{scratch_dragger.js → scratch_dragger.ts} +97 -28
- package/src/{scratch_variable_map.js → scratch_variable_map.ts} +4 -1
- package/src/scratch_variable_model.ts +30 -0
- package/src/{shadows.js → shadows.ts} +8 -4
- package/src/{status_indicator_label.js → status_indicator_label.ts} +24 -36
- package/src/{status_indicator_label_flyout_inflater.js → status_indicator_label_flyout_inflater.ts} +13 -9
- package/src/{variables.js → variables.ts} +153 -123
- package/tsconfig.json +5 -0
- package/src/categories.js +0 -15
- package/src/checkable_continuous_flyout.js +0 -138
- package/src/events/events_block_comment_resize.js +0 -52
- package/src/events/events_block_drag_end.js +0 -33
- package/src/events/events_block_drag_outside.js +0 -30
- package/src/procedures.js +0 -425
- package/src/recyclable_block_flyout_inflater.js +0 -194
- package/src/renderer/renderer.js +0 -74
- package/src/scratch_blocks_utils.js +0 -148
- package/src/scratch_connection_checker.js +0 -29
- package/src/scratch_continuous_toolbox.js +0 -78
- package/src/scratch_variable_model.js +0 -24
- /package/{continuous-toolbox.d.ts → types/continuous-toolbox.d.ts} +0 -0
|
@@ -23,21 +23,14 @@
|
|
|
23
23
|
* @author pkaplan@media.mit.edu (Paul Kaplan)
|
|
24
24
|
*/
|
|
25
25
|
import * as Blockly from "blockly/core";
|
|
26
|
+
import type { ProcedureDeclarationBlock } from "../blocks/procedures";
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Class for an editable text field displaying a deletion icon when selected.
|
|
29
|
-
* @param {string} text The initial content of the field.
|
|
30
|
-
* @param {Function=} opt_validator An optional function that is called
|
|
31
|
-
* to validate any constraints on what the user entered. Takes the new
|
|
32
|
-
* text as an argument and returns either the accepted text, a replacement
|
|
33
|
-
* text, or null to abort the change.
|
|
34
|
-
* @param {RegExp=} opt_restrictor An optional regular expression to restrict
|
|
35
|
-
* typed text to. Text that doesn't match the restrictor will never show
|
|
36
|
-
* in the text field.
|
|
37
|
-
* @extends {Blockly.FieldTextInput}
|
|
38
|
-
* @constructor
|
|
39
30
|
*/
|
|
40
31
|
export class FieldTextInputRemovable extends Blockly.FieldTextInput {
|
|
32
|
+
private removeButtonMouseWrapper_?: Blockly.browserEvents.Data;
|
|
33
|
+
|
|
41
34
|
/**
|
|
42
35
|
* Show the inline free-text editor on top of the text with the remove button.
|
|
43
36
|
*/
|
|
@@ -68,11 +61,12 @@ export class FieldTextInputRemovable extends Blockly.FieldTextInput {
|
|
|
68
61
|
/**
|
|
69
62
|
* Function to call when remove button is called. Checks for removeFieldCallback
|
|
70
63
|
* on sourceBlock and calls it if possible.
|
|
71
|
-
* @private
|
|
72
64
|
*/
|
|
73
|
-
removeCallback_() {
|
|
74
|
-
if (this.sourceBlock_ && this.sourceBlock_
|
|
75
|
-
this.sourceBlock_.removeFieldCallback(
|
|
65
|
+
private removeCallback_() {
|
|
66
|
+
if (this.sourceBlock_ && "removeFieldCallback" in this.sourceBlock_) {
|
|
67
|
+
(this.sourceBlock_ as ProcedureDeclarationBlock).removeFieldCallback(
|
|
68
|
+
this
|
|
69
|
+
);
|
|
76
70
|
} else {
|
|
77
71
|
console.warn("Expected a source block with removeFieldCallback");
|
|
78
72
|
}
|
|
@@ -81,13 +75,16 @@ export class FieldTextInputRemovable extends Blockly.FieldTextInput {
|
|
|
81
75
|
/**
|
|
82
76
|
* Helper function to construct a FieldTextInputRemovable from a JSON arg object,
|
|
83
77
|
* dereferencing any string table references.
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* @returns
|
|
87
|
-
* @public
|
|
78
|
+
*
|
|
79
|
+
* @param options A JSON object with options (text, class, and spellcheck).
|
|
80
|
+
* @returns The new text input.
|
|
88
81
|
*/
|
|
89
|
-
fromJson(
|
|
90
|
-
|
|
82
|
+
fromJson(
|
|
83
|
+
options: Blockly.FieldTextInputFromJsonConfig
|
|
84
|
+
): FieldTextInputRemovable {
|
|
85
|
+
const text = Blockly.utils.parsing.replaceMessageReferences(
|
|
86
|
+
options["text"]
|
|
87
|
+
);
|
|
91
88
|
const field = new FieldTextInputRemovable(text, null, options);
|
|
92
89
|
if (typeof options["spellcheck"] == "boolean") {
|
|
93
90
|
field.setSpellcheck(options["spellcheck"]);
|
|
@@ -27,29 +27,36 @@ import * as Blockly from "blockly/core";
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Class for a variable getter field.
|
|
30
|
-
* @param {string} allowedVariableType The type of variables this field can display.
|
|
31
30
|
*/
|
|
32
31
|
class FieldVariableGetter extends Blockly.FieldLabel {
|
|
33
|
-
|
|
32
|
+
private variable: Blockly.IVariableModel<Blockly.IVariableState> | null =
|
|
33
|
+
null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new FieldVariableGetter.
|
|
37
|
+
*
|
|
38
|
+
* @param allowedVariableType The type of variables this field can display.
|
|
39
|
+
*/
|
|
40
|
+
constructor(private allowedVariableType = "") {
|
|
34
41
|
super(Blockly.Field.SKIP_SETUP);
|
|
35
42
|
this.SERIALIZABLE = true;
|
|
36
|
-
this.allowedVariableType = allowedVariableType;
|
|
37
|
-
this.variable = null;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/**
|
|
41
46
|
* Returns the ID of this field's variable.
|
|
42
|
-
*
|
|
47
|
+
*
|
|
48
|
+
* @returns The ID of this field's variable.
|
|
43
49
|
*/
|
|
44
|
-
getValue() {
|
|
50
|
+
getValue(): string {
|
|
45
51
|
return this.variable?.getId() ?? "";
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
/**
|
|
49
55
|
* Returns the name of this field's variable.
|
|
50
|
-
*
|
|
56
|
+
*
|
|
57
|
+
* @returns The name of this field's variable.
|
|
51
58
|
*/
|
|
52
|
-
getText() {
|
|
59
|
+
getText(): string {
|
|
53
60
|
return this.variable?.getName() ?? "";
|
|
54
61
|
}
|
|
55
62
|
|
|
@@ -57,19 +64,19 @@ class FieldVariableGetter extends Blockly.FieldLabel {
|
|
|
57
64
|
* Get the variable model for the variable associated with this field.
|
|
58
65
|
* Not guaranteed to be in the variable map on the workspace (e.g. if accessed
|
|
59
66
|
* after the variable has been deleted).
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* @package
|
|
67
|
+
*
|
|
68
|
+
* @returns the selected variable, or null if none was selected.
|
|
63
69
|
*/
|
|
64
|
-
getVariable() {
|
|
70
|
+
getVariable(): Blockly.IVariableModel<Blockly.IVariableState> | null {
|
|
65
71
|
return this.variable;
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
/**
|
|
69
75
|
* Updates this field's variable to one with the given ID.
|
|
70
|
-
*
|
|
76
|
+
*
|
|
77
|
+
* @param newVariableId ID of a variable this field should represent.
|
|
71
78
|
*/
|
|
72
|
-
doValueUpdate_(newVariableId) {
|
|
79
|
+
doValueUpdate_(newVariableId: string) {
|
|
73
80
|
super.doValueUpdate_(newVariableId);
|
|
74
81
|
const workspace = this.getSourceBlock().workspace;
|
|
75
82
|
this.variable = Blockly.Variables.getVariable(workspace, newVariableId);
|
|
@@ -85,15 +92,15 @@ class FieldVariableGetter extends Blockly.FieldLabel {
|
|
|
85
92
|
this.forceRerender();
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
static fromJson(options) {
|
|
95
|
+
static fromJson(options: FieldVariableGetterConfig) {
|
|
89
96
|
return new FieldVariableGetter(options["allowedVariableType"]);
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
fromXml(element) {
|
|
99
|
+
fromXml(element: Element) {
|
|
93
100
|
this.setValue(element.getAttribute("id"));
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
toXml(element) {
|
|
103
|
+
toXml(element: Element): Element {
|
|
97
104
|
element.setAttribute("id", this.variable.getId());
|
|
98
105
|
element.setAttribute("variabletype", this.variable.getType());
|
|
99
106
|
element.textContent = this.variable.getName();
|
|
@@ -101,6 +108,10 @@ class FieldVariableGetter extends Blockly.FieldLabel {
|
|
|
101
108
|
}
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
interface FieldVariableGetterConfig extends Blockly.FieldLabelConfig {
|
|
112
|
+
allowedVariableType?: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
104
115
|
/**
|
|
105
116
|
* Register the field and any dependencies.
|
|
106
117
|
*/
|
|
@@ -26,10 +26,10 @@ import * as Blockly from "blockly/core";
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Class for a vertical separator line.
|
|
29
|
-
* @extends {Blockly.Field}
|
|
30
|
-
* @constructor
|
|
31
29
|
*/
|
|
32
30
|
class FieldVerticalSeparator extends Blockly.Field {
|
|
31
|
+
private lineElement?: SVGLineElement;
|
|
32
|
+
|
|
33
33
|
constructor() {
|
|
34
34
|
super(Blockly.Field.SKIP_SETUP);
|
|
35
35
|
/**
|
|
@@ -39,17 +39,10 @@ class FieldVerticalSeparator extends Blockly.Field {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Construct a FieldVerticalSeparator
|
|
43
|
-
* @
|
|
44
|
-
* by Field.fromJson).
|
|
45
|
-
* @returns {!Blockly.FieldVerticalSeparator} The new field instance.
|
|
46
|
-
* @package
|
|
47
|
-
* @nocollapse
|
|
42
|
+
* Construct a FieldVerticalSeparator.
|
|
43
|
+
* @returns The new field instance.
|
|
48
44
|
*/
|
|
49
|
-
static fromJson = function (
|
|
50
|
-
/* eslint-disable no-unused-vars */ _element
|
|
51
|
-
/* eslint-enable no-unused-vars */
|
|
52
|
-
) {
|
|
45
|
+
static fromJson = function () {
|
|
53
46
|
return new FieldVerticalSeparator();
|
|
54
47
|
};
|
|
55
48
|
|
|
@@ -57,14 +50,14 @@ class FieldVerticalSeparator extends Blockly.Field {
|
|
|
57
50
|
* Install this field on a block.
|
|
58
51
|
*/
|
|
59
52
|
initView() {
|
|
60
|
-
const height =
|
|
53
|
+
const height =
|
|
54
|
+
10 * (this.getConstants() as Blockly.zelos.ConstantProvider).GRID_UNIT;
|
|
61
55
|
this.size_ = new Blockly.utils.Size(1, height);
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
this.lineElement_ = Blockly.utils.dom.createSvgElement(
|
|
57
|
+
this.lineElement = Blockly.utils.dom.createSvgElement(
|
|
65
58
|
"line",
|
|
66
59
|
{
|
|
67
|
-
stroke: this.sourceBlock_.getColourSecondary(),
|
|
60
|
+
stroke: (this.sourceBlock_ as Blockly.BlockSvg).getColourSecondary(),
|
|
68
61
|
"stroke-linecap": "round",
|
|
69
62
|
x1: 0,
|
|
70
63
|
y1: 0,
|
|
@@ -79,19 +72,17 @@ class FieldVerticalSeparator extends Blockly.Field {
|
|
|
79
72
|
* Set the height of the line element, without adjusting the field's height.
|
|
80
73
|
* This allows the line's height to be changed without causing it to be
|
|
81
74
|
* centered with the new height (needed for correct rendering of hat blocks).
|
|
82
|
-
* @param
|
|
75
|
+
* @param newHeight the new height for the line.
|
|
83
76
|
* @package
|
|
84
77
|
*/
|
|
85
|
-
setLineHeight(newHeight) {
|
|
86
|
-
this.
|
|
78
|
+
setLineHeight(newHeight: number) {
|
|
79
|
+
this.lineElement.setAttribute("y2", `${newHeight}`);
|
|
87
80
|
}
|
|
88
81
|
|
|
89
82
|
/**
|
|
90
83
|
* Get the value of this field. A no-op in this case.
|
|
91
|
-
* @return {string} null.
|
|
92
|
-
* @override
|
|
93
84
|
*/
|
|
94
|
-
getValue() {
|
|
85
|
+
getValue(): string | null {
|
|
95
86
|
return null;
|
|
96
87
|
}
|
|
97
88
|
|
|
@@ -101,19 +92,13 @@ class FieldVerticalSeparator extends Blockly.Field {
|
|
|
101
92
|
|
|
102
93
|
/**
|
|
103
94
|
* Set the value of this field. A no-op in this case.
|
|
104
|
-
* @param {?string} src New value.
|
|
105
|
-
* @override
|
|
106
95
|
*/
|
|
107
|
-
setValue(
|
|
108
|
-
/* eslint-disable no-unused-vars */ src
|
|
109
|
-
/* eslint-enable no-unused-vars */
|
|
110
|
-
) {
|
|
96
|
+
setValue() {
|
|
111
97
|
return;
|
|
112
98
|
}
|
|
113
99
|
|
|
114
100
|
/**
|
|
115
101
|
* Separator lines are fixed width, no need to render.
|
|
116
|
-
* @private
|
|
117
102
|
*/
|
|
118
103
|
render_() {
|
|
119
104
|
// NOP
|
|
@@ -121,7 +106,6 @@ class FieldVerticalSeparator extends Blockly.Field {
|
|
|
121
106
|
|
|
122
107
|
/**
|
|
123
108
|
* Separator lines are fixed width, no need to update.
|
|
124
|
-
* @private
|
|
125
109
|
*/
|
|
126
110
|
updateWidth() {
|
|
127
111
|
// NOP
|
|
@@ -26,17 +26,42 @@
|
|
|
26
26
|
|
|
27
27
|
import * as Blockly from "blockly/core";
|
|
28
28
|
|
|
29
|
-
class
|
|
29
|
+
class ScratchFieldAngle extends Blockly.FieldNumber {
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* @returns {!Blockly.FieldAngle} The new field instance.
|
|
34
|
-
* @package
|
|
35
|
-
* @nocollapse
|
|
31
|
+
* The highlighted portion of the angle picker circle, between 0º and the
|
|
32
|
+
* selected angle.
|
|
36
33
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
private gauge?: SVGPathElement;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The line to the angle picker handle.
|
|
38
|
+
*/
|
|
39
|
+
private line?: SVGLineElement;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The grabbable handle used to choose an angle.
|
|
43
|
+
*/
|
|
44
|
+
private handle?: SVGGElement;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The arrow graphic shown on the grab handle.
|
|
48
|
+
*/
|
|
49
|
+
private arrow?: SVGImageElement;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Opaque identifier used to unbind event listener in dispose().
|
|
53
|
+
*/
|
|
54
|
+
private mouseDownWrapper_: Blockly.browserEvents.Data;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Opaque identifier used to unbind event listener in dispose().
|
|
58
|
+
*/
|
|
59
|
+
private mouseMoveWrapper: Blockly.browserEvents.Data;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Opaque identifier used to unbind event listener in dispose().
|
|
63
|
+
*/
|
|
64
|
+
private mouseUpWrapper: Blockly.browserEvents.Data;
|
|
40
65
|
|
|
41
66
|
/**
|
|
42
67
|
* Round angles to the nearest 15 degrees when using mouse.
|
|
@@ -89,7 +114,8 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
89
114
|
ARROW_WIDTH = this.HANDLE_RADIUS;
|
|
90
115
|
|
|
91
116
|
/**
|
|
92
|
-
* Half the stroke-width used for the "glow" around the drag handle, rounded
|
|
117
|
+
* Half the stroke-width used for the "glow" around the drag handle, rounded
|
|
118
|
+
* up to nearest whole pixel.
|
|
93
119
|
*/
|
|
94
120
|
|
|
95
121
|
HANDLE_GLOW_WIDTH = 3;
|
|
@@ -111,36 +137,33 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
111
137
|
ARROW_SVG_PATH = "icons/arrow.svg";
|
|
112
138
|
|
|
113
139
|
/**
|
|
114
|
-
* Clean up this FieldAngle, as well as the inherited
|
|
115
|
-
* @return {!Function} Closure to call on destruction of the WidgetDiv.
|
|
116
|
-
* @private
|
|
140
|
+
* Clean up this FieldAngle, as well as the inherited FieldNumber.
|
|
117
141
|
*/
|
|
118
142
|
dispose() {
|
|
119
143
|
super.dispose();
|
|
120
|
-
this.
|
|
144
|
+
this.gauge = null;
|
|
121
145
|
if (this.mouseDownWrapper_) {
|
|
122
146
|
Blockly.browserEvents.unbind(this.mouseDownWrapper_);
|
|
123
147
|
}
|
|
124
|
-
if (this.
|
|
125
|
-
Blockly.browserEvents.unbind(this.
|
|
148
|
+
if (this.mouseUpWrapper) {
|
|
149
|
+
Blockly.browserEvents.unbind(this.mouseUpWrapper);
|
|
126
150
|
}
|
|
127
|
-
if (this.
|
|
128
|
-
Blockly.browserEvents.unbind(this.
|
|
151
|
+
if (this.mouseMoveWrapper) {
|
|
152
|
+
Blockly.browserEvents.unbind(this.mouseMoveWrapper);
|
|
129
153
|
}
|
|
130
154
|
}
|
|
131
155
|
|
|
132
156
|
/**
|
|
133
157
|
* Show the inline free-text editor on top of the text.
|
|
134
|
-
* @private
|
|
135
158
|
*/
|
|
136
|
-
showEditor_(event) {
|
|
159
|
+
showEditor_(event: PointerEvent) {
|
|
137
160
|
super.showEditor_(event);
|
|
138
161
|
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
|
|
139
162
|
Blockly.DropDownDiv.hideWithoutAnimation();
|
|
140
163
|
Blockly.DropDownDiv.clearContent();
|
|
141
|
-
|
|
164
|
+
const div = Blockly.DropDownDiv.getContentDiv();
|
|
142
165
|
// Build the SVG DOM.
|
|
143
|
-
|
|
166
|
+
const svg = Blockly.utils.dom.createSvgElement(
|
|
144
167
|
"svg",
|
|
145
168
|
{
|
|
146
169
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -158,19 +181,23 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
158
181
|
cx: this.HALF,
|
|
159
182
|
cy: this.HALF,
|
|
160
183
|
r: this.RADIUS,
|
|
161
|
-
fill:
|
|
162
|
-
|
|
184
|
+
fill: (
|
|
185
|
+
this.getSourceBlock().getParent() as Blockly.BlockSvg
|
|
186
|
+
).getColourSecondary(),
|
|
187
|
+
stroke: (
|
|
188
|
+
this.getSourceBlock().getParent() as Blockly.BlockSvg
|
|
189
|
+
).getColourTertiary(),
|
|
163
190
|
class: "blocklyAngleCircle",
|
|
164
191
|
},
|
|
165
192
|
svg
|
|
166
193
|
);
|
|
167
|
-
this.
|
|
194
|
+
this.gauge = Blockly.utils.dom.createSvgElement(
|
|
168
195
|
"path",
|
|
169
196
|
{ class: "blocklyAngleGauge" },
|
|
170
197
|
svg
|
|
171
198
|
);
|
|
172
|
-
// The moving line, x2 and y2 are set in
|
|
173
|
-
this.
|
|
199
|
+
// The moving line, x2 and y2 are set in updateGraph
|
|
200
|
+
this.line = Blockly.utils.dom.createSvgElement(
|
|
174
201
|
"line",
|
|
175
202
|
{
|
|
176
203
|
x1: this.HALF,
|
|
@@ -180,7 +207,7 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
180
207
|
svg
|
|
181
208
|
);
|
|
182
209
|
// The fixed vertical line at the offset
|
|
183
|
-
|
|
210
|
+
const offsetRadians = (Math.PI * this.OFFSET) / 180;
|
|
184
211
|
Blockly.utils.dom.createSvgElement(
|
|
185
212
|
"line",
|
|
186
213
|
{
|
|
@@ -193,7 +220,7 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
193
220
|
svg
|
|
194
221
|
);
|
|
195
222
|
// Draw markers around the edge.
|
|
196
|
-
for (
|
|
223
|
+
for (let angle = 0; angle < 360; angle += 15) {
|
|
197
224
|
Blockly.utils.dom.createSvgElement(
|
|
198
225
|
"line",
|
|
199
226
|
{
|
|
@@ -220,7 +247,7 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
220
247
|
svg
|
|
221
248
|
);
|
|
222
249
|
// Handle group: a circle and the arrow image
|
|
223
|
-
this.
|
|
250
|
+
this.handle = Blockly.utils.dom.createSvgElement("g", {}, svg);
|
|
224
251
|
Blockly.utils.dom.createSvgElement(
|
|
225
252
|
"circle",
|
|
226
253
|
{
|
|
@@ -229,9 +256,9 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
229
256
|
r: this.HANDLE_RADIUS,
|
|
230
257
|
class: "blocklyAngleDragHandle",
|
|
231
258
|
},
|
|
232
|
-
this.
|
|
259
|
+
this.handle
|
|
233
260
|
);
|
|
234
|
-
this.
|
|
261
|
+
this.arrow = Blockly.utils.dom.createSvgElement(
|
|
235
262
|
"image",
|
|
236
263
|
{
|
|
237
264
|
width: this.ARROW_WIDTH,
|
|
@@ -240,9 +267,9 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
240
267
|
y: -this.ARROW_WIDTH / 2,
|
|
241
268
|
class: "blocklyAngleDragArrow",
|
|
242
269
|
},
|
|
243
|
-
this.
|
|
270
|
+
this.handle
|
|
244
271
|
);
|
|
245
|
-
this.
|
|
272
|
+
this.arrow.setAttributeNS(
|
|
246
273
|
"http://www.w3.org/1999/xlink",
|
|
247
274
|
"xlink:href",
|
|
248
275
|
Blockly.getMainWorkspace().options.pathToMedia + this.ARROW_SVG_PATH
|
|
@@ -250,32 +277,36 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
250
277
|
|
|
251
278
|
Blockly.DropDownDiv.setColour(
|
|
252
279
|
this.getSourceBlock().getParent().getColour(),
|
|
253
|
-
|
|
280
|
+
(
|
|
281
|
+
this.getSourceBlock().getParent() as Blockly.BlockSvg
|
|
282
|
+
).getColourTertiary()
|
|
283
|
+
);
|
|
284
|
+
Blockly.DropDownDiv.showPositionedByBlock(
|
|
285
|
+
this,
|
|
286
|
+
this.getSourceBlock() as Blockly.BlockSvg
|
|
254
287
|
);
|
|
255
|
-
Blockly.DropDownDiv.showPositionedByBlock(this, this.getSourceBlock());
|
|
256
288
|
|
|
257
289
|
this.mouseDownWrapper_ = Blockly.browserEvents.bind(
|
|
258
|
-
this.
|
|
290
|
+
this.handle,
|
|
259
291
|
"mousedown",
|
|
260
292
|
this,
|
|
261
293
|
this.onMouseDown
|
|
262
294
|
);
|
|
263
295
|
|
|
264
|
-
this.
|
|
296
|
+
this.updateGraph();
|
|
265
297
|
}
|
|
266
298
|
|
|
267
299
|
/**
|
|
268
300
|
* Set the angle to match the mouse's position.
|
|
269
|
-
* @param {!Event} e Mouse move event.
|
|
270
301
|
*/
|
|
271
302
|
onMouseDown() {
|
|
272
|
-
this.
|
|
303
|
+
this.mouseMoveWrapper = Blockly.browserEvents.bind(
|
|
273
304
|
document.body,
|
|
274
305
|
"mousemove",
|
|
275
306
|
this,
|
|
276
307
|
this.onMouseMove
|
|
277
308
|
);
|
|
278
|
-
this.
|
|
309
|
+
this.mouseUpWrapper = Blockly.browserEvents.bind(
|
|
279
310
|
document.body,
|
|
280
311
|
"mouseup",
|
|
281
312
|
this,
|
|
@@ -285,23 +316,22 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
285
316
|
|
|
286
317
|
/**
|
|
287
318
|
* Set the angle to match the mouse's position.
|
|
288
|
-
* @param {!Event} e Mouse move event.
|
|
289
319
|
*/
|
|
290
320
|
onMouseUp() {
|
|
291
|
-
Blockly.browserEvents.unbind(this.
|
|
292
|
-
Blockly.browserEvents.unbind(this.
|
|
321
|
+
Blockly.browserEvents.unbind(this.mouseMoveWrapper);
|
|
322
|
+
Blockly.browserEvents.unbind(this.mouseUpWrapper);
|
|
293
323
|
}
|
|
294
324
|
|
|
295
325
|
/**
|
|
296
326
|
* Set the angle to match the mouse's position.
|
|
297
|
-
* @param
|
|
327
|
+
* @param e Mouse move event.
|
|
298
328
|
*/
|
|
299
|
-
onMouseMove(e) {
|
|
329
|
+
onMouseMove(e: PointerEvent) {
|
|
300
330
|
e.preventDefault();
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
331
|
+
const bBox = this.gauge.ownerSVGElement.getBoundingClientRect();
|
|
332
|
+
const dx = e.clientX - bBox.left - this.HALF;
|
|
333
|
+
const dy = e.clientY - bBox.top - this.HALF;
|
|
334
|
+
let angle = Math.atan(-dy / dx);
|
|
305
335
|
if (isNaN(angle)) {
|
|
306
336
|
// This shouldn't happen, but let's not let this error propagate further.
|
|
307
337
|
return;
|
|
@@ -328,29 +358,28 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
328
358
|
|
|
329
359
|
/**
|
|
330
360
|
* Redraw the graph with the current angle.
|
|
331
|
-
* @private
|
|
332
361
|
*/
|
|
333
|
-
|
|
334
|
-
if (!this.
|
|
362
|
+
private updateGraph() {
|
|
363
|
+
if (!this.gauge) {
|
|
335
364
|
return;
|
|
336
365
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
366
|
+
const angleDegrees = (Number(this.getValue()) % 360) + this.OFFSET;
|
|
367
|
+
let angleRadians = this.toRadians(angleDegrees);
|
|
368
|
+
const path = ["M ", this.HALF, ",", this.HALF];
|
|
369
|
+
let x2 = this.HALF;
|
|
370
|
+
let y2 = this.HALF;
|
|
342
371
|
if (!isNaN(angleRadians)) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
372
|
+
const angle1 = this.toRadians(this.OFFSET);
|
|
373
|
+
const x1 = Math.cos(angle1) * this.RADIUS;
|
|
374
|
+
const y1 = Math.sin(angle1) * -this.RADIUS;
|
|
346
375
|
if (this.CLOCKWISE) {
|
|
347
376
|
angleRadians = 2 * angle1 - angleRadians;
|
|
348
377
|
}
|
|
349
378
|
x2 += Math.cos(angleRadians) * this.RADIUS;
|
|
350
379
|
y2 -= Math.sin(angleRadians) * this.RADIUS;
|
|
351
380
|
// Use large arc only if input value is greater than wrap
|
|
352
|
-
|
|
353
|
-
|
|
381
|
+
const largeFlag = Math.abs(angleDegrees - this.OFFSET) > 180 ? 1 : 0;
|
|
382
|
+
let sweepFlag = Number(this.CLOCKWISE);
|
|
354
383
|
if (angleDegrees < this.OFFSET) {
|
|
355
384
|
sweepFlag = 1 - sweepFlag; // Sweep opposite direction if less than the offset
|
|
356
385
|
}
|
|
@@ -375,29 +404,30 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
375
404
|
);
|
|
376
405
|
|
|
377
406
|
// Image rotation needs to be set in degrees
|
|
407
|
+
let imageRotation: number;
|
|
378
408
|
if (this.CLOCKWISE) {
|
|
379
|
-
|
|
409
|
+
imageRotation = angleDegrees + 2 * this.OFFSET;
|
|
380
410
|
} else {
|
|
381
|
-
|
|
411
|
+
imageRotation = -angleDegrees;
|
|
382
412
|
}
|
|
383
|
-
this.
|
|
413
|
+
this.arrow.setAttribute("transform", "rotate(" + imageRotation + ")");
|
|
384
414
|
}
|
|
385
|
-
this.
|
|
386
|
-
this.
|
|
387
|
-
this.
|
|
388
|
-
this.
|
|
415
|
+
this.gauge.setAttribute("d", path.join(""));
|
|
416
|
+
this.line.setAttribute("x2", `${x2}`);
|
|
417
|
+
this.line.setAttribute("y2", `${y2}`);
|
|
418
|
+
this.handle.setAttribute("transform", "translate(" + x2 + "," + y2 + ")");
|
|
389
419
|
}
|
|
390
420
|
|
|
391
421
|
/**
|
|
392
422
|
* Ensure that only an angle may be entered.
|
|
393
|
-
* @param
|
|
394
|
-
* @
|
|
423
|
+
* @param text The user's text.
|
|
424
|
+
* @returns A string representing a valid angle, or null if invalid.
|
|
395
425
|
*/
|
|
396
|
-
doClassValidation_(text) {
|
|
426
|
+
doClassValidation_(text: string): number | null {
|
|
397
427
|
if (text === null) {
|
|
398
428
|
return null;
|
|
399
429
|
}
|
|
400
|
-
|
|
430
|
+
let n = parseFloat(text || "0");
|
|
401
431
|
if (isNaN(n)) {
|
|
402
432
|
return null;
|
|
403
433
|
}
|
|
@@ -411,23 +441,37 @@ class FieldAngle extends Blockly.FieldNumber {
|
|
|
411
441
|
return Number(n);
|
|
412
442
|
}
|
|
413
443
|
|
|
414
|
-
doValueUpdate_(newValue) {
|
|
444
|
+
doValueUpdate_(newValue: number) {
|
|
415
445
|
super.doValueUpdate_(newValue);
|
|
416
|
-
this.
|
|
446
|
+
this.updateGraph();
|
|
417
447
|
}
|
|
418
448
|
|
|
419
|
-
toDegrees(radians) {
|
|
449
|
+
toDegrees(radians: number) {
|
|
420
450
|
return (radians * 180) / Math.PI;
|
|
421
451
|
}
|
|
422
452
|
|
|
423
|
-
toRadians(degrees) {
|
|
453
|
+
toRadians(degrees: number) {
|
|
424
454
|
return (degrees * Math.PI) / 180;
|
|
425
455
|
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Construct a FieldAngle from a JSON arg object.
|
|
459
|
+
*
|
|
460
|
+
* @param options A JSON object with options (angle).
|
|
461
|
+
* @returns The new field instance.
|
|
462
|
+
*/
|
|
463
|
+
fromJson(options: ScratchFieldAngleJsonConfig): ScratchFieldAngle {
|
|
464
|
+
return new ScratchFieldAngle(options["angle"]);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export interface ScratchFieldAngleJsonConfig {
|
|
469
|
+
angle?: number;
|
|
426
470
|
}
|
|
427
471
|
|
|
428
472
|
/**
|
|
429
473
|
* Register the field and any dependencies.
|
|
430
474
|
*/
|
|
431
|
-
export function
|
|
432
|
-
Blockly.fieldRegistry.register("field_angle",
|
|
475
|
+
export function registerScratchFieldAngle() {
|
|
476
|
+
Blockly.fieldRegistry.register("field_angle", ScratchFieldAngle);
|
|
433
477
|
}
|