scratch-blocks 2.0.1 → 2.0.3
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 +140 -0
- package/commitlint.config.js +2 -2
- package/dist/main.mjs +1 -2
- package/dist/types/msg/scratch_msgs.d.ts.map +1 -1
- package/dist/types/src/block_reporting.d.ts.map +1 -1
- package/dist/types/src/blocks/colour.d.ts +0 -19
- package/dist/types/src/blocks/colour.d.ts.map +1 -1
- package/dist/types/src/blocks/control.d.ts +0 -19
- package/dist/types/src/blocks/control.d.ts.map +1 -1
- package/dist/types/src/blocks/data.d.ts +0 -19
- package/dist/types/src/blocks/data.d.ts.map +1 -1
- package/dist/types/src/blocks/event.d.ts +0 -19
- package/dist/types/src/blocks/event.d.ts.map +1 -1
- package/dist/types/src/blocks/looks.d.ts +0 -19
- package/dist/types/src/blocks/looks.d.ts.map +1 -1
- package/dist/types/src/blocks/math.d.ts +0 -19
- package/dist/types/src/blocks/math.d.ts.map +1 -1
- package/dist/types/src/blocks/matrix.d.ts +0 -19
- package/dist/types/src/blocks/matrix.d.ts.map +1 -1
- package/dist/types/src/blocks/motion.d.ts +0 -19
- package/dist/types/src/blocks/motion.d.ts.map +1 -1
- package/dist/types/src/blocks/note.d.ts +0 -19
- package/dist/types/src/blocks/note.d.ts.map +1 -1
- package/dist/types/src/blocks/operators.d.ts +0 -19
- package/dist/types/src/blocks/operators.d.ts.map +1 -1
- package/dist/types/src/blocks/procedures.d.ts +6 -9
- package/dist/types/src/blocks/procedures.d.ts.map +1 -1
- package/dist/types/src/blocks/sensing.d.ts +0 -19
- package/dist/types/src/blocks/sensing.d.ts.map +1 -1
- package/dist/types/src/blocks/sound.d.ts +0 -19
- package/dist/types/src/blocks/sound.d.ts.map +1 -1
- package/dist/types/src/blocks/text.d.ts +0 -19
- package/dist/types/src/blocks/text.d.ts.map +1 -1
- package/dist/types/src/blocks/vertical_extensions.d.ts +0 -19
- package/dist/types/src/blocks/vertical_extensions.d.ts.map +1 -1
- package/dist/types/src/checkable_continuous_flyout.d.ts +2 -7
- package/dist/types/src/checkable_continuous_flyout.d.ts.map +1 -1
- package/dist/types/src/checkbox_bubble.d.ts +13 -12
- 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/constants.d.ts +0 -7
- package/dist/types/src/constants.d.ts.map +1 -1
- package/dist/types/src/context_menu_items.d.ts +0 -5
- package/dist/types/src/context_menu_items.d.ts.map +1 -1
- package/dist/types/src/data_category.d.ts +2 -4
- package/dist/types/src/data_category.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_base.d.ts +2 -3
- package/dist/types/src/events/events_block_comment_base.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_change.d.ts +0 -5
- package/dist/types/src/events/events_block_comment_change.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_collapse.d.ts +0 -5
- package/dist/types/src/events/events_block_comment_collapse.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_create.d.ts +0 -5
- package/dist/types/src/events/events_block_comment_create.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_delete.d.ts +0 -5
- package/dist/types/src/events/events_block_comment_delete.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_move.d.ts +0 -5
- package/dist/types/src/events/events_block_comment_move.d.ts.map +1 -1
- package/dist/types/src/events/events_block_comment_resize.d.ts +0 -5
- package/dist/types/src/events/events_block_comment_resize.d.ts.map +1 -1
- package/dist/types/src/events/events_block_drag_end.d.ts +1 -2
- 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 -2
- package/dist/types/src/events/events_block_drag_outside.d.ts.map +1 -1
- package/dist/types/src/events/events_scratch_variable_create.d.ts +0 -5
- package/dist/types/src/events/events_scratch_variable_create.d.ts.map +1 -1
- package/dist/types/src/fields/field_colour_slider.d.ts +7 -31
- package/dist/types/src/fields/field_colour_slider.d.ts.map +1 -1
- package/dist/types/src/fields/field_matrix.d.ts +0 -19
- package/dist/types/src/fields/field_matrix.d.ts.map +1 -1
- package/dist/types/src/fields/field_note.d.ts +8 -23
- package/dist/types/src/fields/field_note.d.ts.map +1 -1
- package/dist/types/src/fields/field_textinput_removable.d.ts +2 -4
- package/dist/types/src/fields/field_textinput_removable.d.ts.map +1 -1
- package/dist/types/src/fields/field_variable_getter.d.ts +0 -19
- package/dist/types/src/fields/field_variable_getter.d.ts.map +1 -1
- package/dist/types/src/fields/field_vertical_separator.d.ts +0 -19
- package/dist/types/src/fields/field_vertical_separator.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_angle.d.ts +0 -19
- package/dist/types/src/fields/scratch_field_angle.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_dropdown.d.ts +0 -5
- package/dist/types/src/fields/scratch_field_dropdown.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_number.d.ts +0 -19
- package/dist/types/src/fields/scratch_field_number.d.ts.map +1 -1
- package/dist/types/src/fields/scratch_field_variable.d.ts +5 -8
- package/dist/types/src/fields/scratch_field_variable.d.ts.map +1 -1
- package/dist/types/src/flyout_checkbox_icon.d.ts +2 -3
- package/dist/types/src/flyout_checkbox_icon.d.ts.map +1 -1
- package/dist/types/src/glows.d.ts +1 -3
- package/dist/types/src/glows.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +50 -51
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/procedures.d.ts +6 -8
- package/dist/types/src/procedures.d.ts.map +1 -1
- package/dist/types/src/recyclable_block_flyout_inflater.d.ts +3 -5
- package/dist/types/src/recyclable_block_flyout_inflater.d.ts.map +1 -1
- package/dist/types/src/renderer/bowler_hat.d.ts +2 -3
- package/dist/types/src/renderer/bowler_hat.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/cat_face.d.ts +6 -5
- package/dist/types/src/renderer/cat/cat_face.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/constants.d.ts +2 -2
- package/dist/types/src/renderer/cat/constants.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/drawer.d.ts +4 -5
- package/dist/types/src/renderer/cat/drawer.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/path_object.d.ts +2 -3
- package/dist/types/src/renderer/cat/path_object.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/render_info.d.ts +3 -4
- package/dist/types/src/renderer/cat/render_info.d.ts.map +1 -1
- package/dist/types/src/renderer/cat/renderer.d.ts +7 -8
- package/dist/types/src/renderer/cat/renderer.d.ts.map +1 -1
- package/dist/types/src/renderer/constants.d.ts +41 -0
- package/dist/types/src/renderer/constants.d.ts.map +1 -0
- package/dist/types/src/renderer/drawer.d.ts +7 -6
- package/dist/types/src/renderer/drawer.d.ts.map +1 -1
- package/dist/types/src/renderer/path_object.d.ts +1 -3
- package/dist/types/src/renderer/path_object.d.ts.map +1 -1
- package/dist/types/src/renderer/render_info.d.ts +3 -4
- package/dist/types/src/renderer/render_info.d.ts.map +1 -1
- package/dist/types/src/renderer/renderer.d.ts +9 -16
- package/dist/types/src/renderer/renderer.d.ts.map +1 -1
- package/dist/types/src/scratch_block_paster.d.ts +0 -5
- package/dist/types/src/scratch_block_paster.d.ts.map +1 -1
- package/dist/types/src/scratch_blocks_utils.d.ts +0 -20
- package/dist/types/src/scratch_blocks_utils.d.ts.map +1 -1
- package/dist/types/src/scratch_comment_bubble.d.ts +3 -6
- package/dist/types/src/scratch_comment_bubble.d.ts.map +1 -1
- package/dist/types/src/scratch_comment_icon.d.ts +3 -4
- package/dist/types/src/scratch_comment_icon.d.ts.map +1 -1
- package/dist/types/src/scratch_connection_checker.d.ts +0 -5
- package/dist/types/src/scratch_connection_checker.d.ts.map +1 -1
- package/dist/types/src/scratch_continuous_category.d.ts +5 -5
- package/dist/types/src/scratch_continuous_category.d.ts.map +1 -1
- package/dist/types/src/scratch_continuous_toolbox.d.ts +3 -6
- package/dist/types/src/scratch_continuous_toolbox.d.ts.map +1 -1
- package/dist/types/src/scratch_dragger.d.ts +2 -11
- package/dist/types/src/scratch_dragger.d.ts.map +1 -1
- package/dist/types/src/scratch_insertion_marker_previewer.d.ts +0 -5
- package/dist/types/src/scratch_insertion_marker_previewer.d.ts.map +1 -1
- package/dist/types/src/scratch_variable_map.d.ts +0 -5
- package/dist/types/src/scratch_variable_map.d.ts.map +1 -1
- package/dist/types/src/scratch_variable_model.d.ts +2 -3
- package/dist/types/src/scratch_variable_model.d.ts.map +1 -1
- package/dist/types/src/scratch_zoom_controls.d.ts +4 -6
- package/dist/types/src/scratch_zoom_controls.d.ts.map +1 -1
- package/dist/types/src/shadows.d.ts +2 -2
- package/dist/types/src/shadows.d.ts.map +1 -1
- package/dist/types/src/status_indicator_label.d.ts +4 -6
- package/dist/types/src/status_indicator_label.d.ts.map +1 -1
- package/dist/types/src/status_indicator_label_flyout_inflater.d.ts +1 -6
- package/dist/types/src/status_indicator_label_flyout_inflater.d.ts.map +1 -1
- package/dist/types/src/variables.d.ts +4 -8
- package/dist/types/src/variables.d.ts.map +1 -1
- package/dist/types/src/xml.d.ts +2 -3
- package/dist/types/src/xml.d.ts.map +1 -1
- package/dist/types/tests/jsunit/block_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/connection_db_test.d.ts +3 -3
- package/dist/types/tests/jsunit/connection_db_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/connection_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/event_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/extensions_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/field_number_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/field_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/field_variable_getter_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/field_variable_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/gesture_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/input_test.d.ts +1 -0
- package/dist/types/tests/jsunit/input_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/json_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/names_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/procedure_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/scratch_block_comment_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/svg_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/test_utilities.d.ts.map +1 -1
- package/dist/types/tests/jsunit/utils_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/variable_map_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/variable_model_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/widget_div_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/workspace_comment_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/workspace_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/workspace_undo_redo_test.d.ts.map +1 -1
- package/dist/types/tests/jsunit/xml_test.d.ts.map +1 -1
- package/dist/types/tests/workspace_svg/workspace_svg_test.d.ts.map +1 -1
- package/eslint.config.mjs +69 -0
- package/i18n/create_scratch_msgs.js +44 -45
- package/i18n/js_to_json.js +40 -32
- package/i18n/json_to_js.js +37 -37
- package/i18n/sync_tx_translations.js +64 -65
- package/i18n/test_scratch_msgs.js +66 -63
- package/msg/js/en.js +289 -287
- package/msg/json/en.json +284 -284
- package/msg/messages.js +289 -287
- package/msg/scratch_msgs.js +22959 -22970
- package/package.json +13 -10
- package/prettier.config.mjs +3 -0
- package/release.config.js +7 -7
- package/renovate.json5 +7 -9
- package/src/block_reporting.ts +16 -19
- package/src/blocks/colour.ts +12 -15
- package/src/blocks/control.ts +171 -174
- package/src/blocks/data.ts +233 -272
- package/src/blocks/event.ts +121 -123
- package/src/blocks/looks.ts +165 -167
- package/src/blocks/math.ts +44 -46
- package/src/blocks/matrix.ts +11 -13
- package/src/blocks/motion.ts +151 -153
- package/src/blocks/note.ts +11 -13
- package/src/blocks/operators.ts +158 -160
- package/src/blocks/procedures.ts +421 -511
- package/src/blocks/sensing.ts +163 -165
- package/src/blocks/sound.ts +58 -60
- package/src/blocks/text.ts +10 -12
- package/src/blocks/vertical_extensions.ts +86 -102
- package/src/checkable_continuous_flyout.ts +25 -42
- package/src/checkbox_bubble.ts +83 -100
- package/src/colours.ts +35 -37
- package/src/constants.ts +22 -29
- package/src/context_menu_items.ts +59 -84
- package/src/css.ts +3 -4
- package/src/data_category.ts +137 -251
- package/src/events/events_block_comment_base.ts +23 -30
- package/src/events/events_block_comment_change.ts +21 -42
- package/src/events/events_block_comment_collapse.ts +27 -36
- package/src/events/events_block_comment_create.ts +32 -46
- package/src/events/events_block_comment_delete.ts +12 -19
- package/src/events/events_block_comment_move.ts +27 -52
- package/src/events/events_block_comment_resize.ts +28 -55
- package/src/events/events_block_drag_end.ts +16 -26
- package/src/events/events_block_drag_outside.ts +12 -22
- package/src/events/events_scratch_variable_create.ts +37 -42
- package/src/fields/field_colour_slider.ts +186 -216
- package/src/fields/field_matrix.ts +202 -270
- package/src/fields/field_note.ts +277 -375
- package/src/fields/field_textinput_removable.ts +25 -40
- package/src/fields/field_variable_getter.ts +26 -31
- package/src/fields/field_vertical_separator.ts +19 -24
- package/src/fields/scratch_field_angle.ts +151 -187
- package/src/fields/scratch_field_dropdown.ts +15 -19
- package/src/fields/scratch_field_number.ts +125 -181
- package/src/fields/scratch_field_variable.ts +57 -75
- package/src/flyout_checkbox_icon.ts +18 -28
- package/src/glows.ts +52 -59
- package/src/index.ts +119 -133
- package/src/procedures.ts +150 -209
- package/src/recyclable_block_flyout_inflater.ts +14 -25
- package/src/renderer/bowler_hat.ts +6 -8
- package/src/renderer/cat/cat_face.ts +98 -99
- package/src/renderer/cat/constants.ts +67 -87
- package/src/renderer/cat/drawer.ts +21 -27
- package/src/renderer/cat/path_object.ts +3 -5
- package/src/renderer/cat/render_info.ts +5 -8
- package/src/renderer/cat/renderer.ts +12 -16
- package/src/renderer/constants.ts +34 -49
- package/src/renderer/drawer.ts +35 -51
- package/src/renderer/path_object.ts +4 -10
- package/src/renderer/render_info.ts +39 -56
- package/src/renderer/renderer.ts +17 -30
- package/src/scratch_block_paster.ts +12 -20
- package/src/scratch_blocks_utils.ts +4 -7
- package/src/scratch_comment_bubble.ts +76 -105
- package/src/scratch_comment_icon.ts +75 -124
- package/src/scratch_connection_checker.ts +7 -17
- package/src/scratch_continuous_category.ts +24 -28
- package/src/scratch_continuous_toolbox.ts +20 -27
- package/src/scratch_dragger.ts +42 -81
- package/src/scratch_insertion_marker_previewer.ts +6 -11
- package/src/scratch_variable_map.ts +5 -12
- package/src/scratch_variable_model.ts +6 -13
- package/src/scratch_zoom_controls.ts +101 -156
- package/src/shadows.ts +32 -37
- package/src/status_indicator_label.ts +54 -67
- package/src/status_indicator_label_flyout_inflater.ts +11 -21
- package/src/variables.ts +92 -141
- package/src/xml.ts +21 -35
- package/tsconfig.json +3 -7
- package/types/continuous-toolbox.d.ts +1 -1
- package/dist/main.mjs.LICENSE.txt +0 -163
package/src/fields/field_note.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license
|
|
3
2
|
* Visual Blocks Editor
|
|
4
3
|
*
|
|
5
4
|
* Copyright 2018 Massachusetts Institute of Technology
|
|
@@ -17,421 +16,377 @@
|
|
|
17
16
|
* See the License for the specific language governing permissions and
|
|
18
17
|
* limitations under the License.
|
|
19
18
|
*/
|
|
20
|
-
|
|
21
19
|
/**
|
|
22
|
-
* @
|
|
20
|
+
* @file Note input field, for selecting a musical note on a piano.
|
|
23
21
|
* @author ericr@media.mit.edu (Eric Rosenbaum)
|
|
24
22
|
*/
|
|
25
|
-
import * as Blockly from
|
|
23
|
+
import * as Blockly from 'blockly/core'
|
|
26
24
|
|
|
27
25
|
/**
|
|
28
26
|
* Class for a note input field, for selecting a musical note on a piano.
|
|
29
|
-
* @param
|
|
27
|
+
* @param opt_value The initial content of the field. The
|
|
30
28
|
* value should cast to a number, and if it does not, '0' will be used.
|
|
31
|
-
* @param
|
|
29
|
+
* @param opt_validator An optional function that is called
|
|
32
30
|
* to validate any constraints on what the user entered. Takes the new
|
|
33
31
|
* text as an argument and returns the accepted text or null to abort
|
|
34
32
|
* the change.
|
|
35
|
-
* @extends {Blockly.FieldTextInput}
|
|
36
|
-
* @constructor
|
|
37
33
|
*/
|
|
38
34
|
export class FieldNote extends Blockly.FieldTextInput {
|
|
39
35
|
/**
|
|
40
36
|
* Width of the field. Computed when drawing it, and used for animation.
|
|
41
37
|
*/
|
|
42
|
-
private fieldEditorWidth_ = 0
|
|
38
|
+
private fieldEditorWidth_ = 0
|
|
43
39
|
|
|
44
40
|
/**
|
|
45
41
|
* Height of the field. Computed when drawing it.
|
|
46
42
|
*/
|
|
47
|
-
private fieldEditorHeight_ = 0
|
|
43
|
+
private fieldEditorHeight_ = 0
|
|
48
44
|
|
|
49
45
|
/**
|
|
50
46
|
* The piano SVG.
|
|
51
47
|
*/
|
|
52
|
-
private pianoSVG_: SVGElement | null = null
|
|
48
|
+
private pianoSVG_: SVGElement | null = null
|
|
53
49
|
|
|
54
50
|
/**
|
|
55
51
|
* Array of SVG elements representing the clickable piano keys.
|
|
56
52
|
*/
|
|
57
|
-
private keySVGs_: SVGElement[] = []
|
|
53
|
+
private keySVGs_: SVGElement[] = []
|
|
58
54
|
|
|
59
55
|
/**
|
|
60
56
|
* Note name indicator at the top of the field.
|
|
61
57
|
*/
|
|
62
|
-
private noteNameText_: SVGElement | null = null
|
|
58
|
+
private noteNameText_: SVGElement | null = null
|
|
63
59
|
|
|
64
60
|
/**
|
|
65
61
|
* Note name indicator on the low C key.
|
|
66
62
|
*/
|
|
67
|
-
private lowCText_: SVGElement | null = null
|
|
63
|
+
private lowCText_: SVGElement | null = null
|
|
68
64
|
|
|
69
65
|
/**
|
|
70
66
|
* Note name indicator on the low C key.
|
|
71
67
|
*/
|
|
72
|
-
private highCText_: SVGElement | null = null
|
|
68
|
+
private highCText_: SVGElement | null = null
|
|
73
69
|
|
|
74
70
|
/**
|
|
75
71
|
* Octave number of the currently displayed range of keys.
|
|
76
72
|
*/
|
|
77
|
-
private displayedOctave_: number | null = null
|
|
73
|
+
private displayedOctave_: number | null = null
|
|
78
74
|
|
|
79
75
|
/**
|
|
80
76
|
* Current animation position of the piano SVG, as it shifts left or right to
|
|
81
77
|
* change octaves.
|
|
82
78
|
*/
|
|
83
|
-
private animationPos_ = 0
|
|
79
|
+
private animationPos_ = 0
|
|
84
80
|
|
|
85
81
|
/**
|
|
86
82
|
* Target position for the animation as the piano SVG shifts left or right.
|
|
87
83
|
*/
|
|
88
|
-
private animationTarget_ = 0
|
|
84
|
+
private animationTarget_ = 0
|
|
89
85
|
|
|
90
86
|
/**
|
|
91
87
|
* A flag indicating that the mouse is currently down. Used in combination with
|
|
92
88
|
* mouse enter events to update the key selection while dragging.
|
|
93
89
|
*/
|
|
94
|
-
private mouseIsDown_ = false
|
|
90
|
+
private mouseIsDown_ = false
|
|
95
91
|
|
|
96
92
|
/**
|
|
97
93
|
* An array of wrappers for mouse down events on piano keys.
|
|
98
94
|
*/
|
|
99
|
-
private mouseDownWrappers_: Blockly.browserEvents.Data[] = []
|
|
95
|
+
private mouseDownWrappers_: Blockly.browserEvents.Data[] = []
|
|
100
96
|
|
|
101
97
|
/**
|
|
102
98
|
* A wrapper for the mouse up event.
|
|
103
99
|
*/
|
|
104
|
-
private mouseUpWrapper_: Blockly.browserEvents.Data | null = null
|
|
100
|
+
private mouseUpWrapper_: Blockly.browserEvents.Data | null = null
|
|
105
101
|
|
|
106
102
|
/**
|
|
107
103
|
* An array of wrappers for mouse enter events on piano keys.
|
|
108
104
|
*/
|
|
109
|
-
private mouseEnterWrappers_: Blockly.browserEvents.Data[] = []
|
|
105
|
+
private mouseEnterWrappers_: Blockly.browserEvents.Data[] = []
|
|
110
106
|
|
|
111
107
|
/**
|
|
112
108
|
* A wrapper for the mouse down event on the octave down button.
|
|
113
109
|
*/
|
|
114
|
-
private octaveDownMouseDownWrapper_: Blockly.browserEvents.Data | null = null
|
|
110
|
+
private octaveDownMouseDownWrapper_: Blockly.browserEvents.Data | null = null
|
|
115
111
|
|
|
116
112
|
/**
|
|
117
113
|
* A wrapper for the mouse down event on the octave up button.
|
|
118
114
|
*/
|
|
119
|
-
private octaveUpMouseDownWrapper_: Blockly.browserEvents.Data | null = null
|
|
115
|
+
private octaveUpMouseDownWrapper_: Blockly.browserEvents.Data | null = null
|
|
120
116
|
|
|
121
117
|
/**
|
|
122
118
|
* Inset in pixels of content displayed in the field, caused by parent properties.
|
|
123
119
|
* The inset is actually determined by the CSS property blocklyDropDownDiv- it is
|
|
124
120
|
* the sum of the padding and border thickness.
|
|
125
121
|
*/
|
|
126
|
-
static INSET = 5
|
|
122
|
+
static INSET = 5
|
|
127
123
|
|
|
128
124
|
/**
|
|
129
125
|
* Height of the top area of the field, in px.
|
|
130
126
|
*/
|
|
131
|
-
static readonly TOP_MENU_HEIGHT = 32 - FieldNote.INSET
|
|
127
|
+
static readonly TOP_MENU_HEIGHT = 32 - FieldNote.INSET
|
|
132
128
|
|
|
133
129
|
/**
|
|
134
130
|
* Padding on the top and sides of the field, in px.
|
|
135
131
|
*/
|
|
136
|
-
static readonly EDGE_PADDING = 1
|
|
132
|
+
static readonly EDGE_PADDING = 1
|
|
137
133
|
|
|
138
134
|
/**
|
|
139
135
|
* Height of the drop shadow on the piano, in px.
|
|
140
136
|
*/
|
|
141
|
-
static readonly SHADOW_HEIGHT = 4
|
|
137
|
+
static readonly SHADOW_HEIGHT = 4
|
|
142
138
|
|
|
143
139
|
/**
|
|
144
140
|
* Color for the shadow on the piano.
|
|
145
141
|
*/
|
|
146
|
-
static readonly SHADOW_COLOR =
|
|
142
|
+
static readonly SHADOW_COLOR = '#000'
|
|
147
143
|
|
|
148
144
|
/**
|
|
149
145
|
* Opacity for the shadow on the piano.
|
|
150
146
|
*/
|
|
151
|
-
static readonly SHADOW_OPACITY = 0.2
|
|
147
|
+
static readonly SHADOW_OPACITY = 0.2
|
|
152
148
|
|
|
153
149
|
/**
|
|
154
150
|
* A color for the white piano keys.
|
|
155
151
|
*/
|
|
156
|
-
static readonly WHITE_KEY_COLOR =
|
|
152
|
+
static readonly WHITE_KEY_COLOR = '#FFFFFF'
|
|
157
153
|
|
|
158
154
|
/**
|
|
159
155
|
* A color for the black piano keys.
|
|
160
156
|
*/
|
|
161
|
-
static readonly BLACK_KEY_COLOR =
|
|
157
|
+
static readonly BLACK_KEY_COLOR = '#323133'
|
|
162
158
|
|
|
163
159
|
/**
|
|
164
160
|
* A color for stroke around black piano keys.
|
|
165
161
|
*/
|
|
166
|
-
static readonly BLACK_KEY_STROKE =
|
|
162
|
+
static readonly BLACK_KEY_STROKE = '#555555'
|
|
167
163
|
|
|
168
164
|
/**
|
|
169
165
|
* A color for the selected state of a piano key.
|
|
170
166
|
*/
|
|
171
|
-
static readonly KEY_SELECTED_COLOR =
|
|
167
|
+
static readonly KEY_SELECTED_COLOR = '#b0d6ff'
|
|
172
168
|
|
|
173
169
|
/**
|
|
174
170
|
* The number of white keys in one octave on the piano.
|
|
175
171
|
*/
|
|
176
|
-
static readonly NUM_WHITE_KEYS = 8
|
|
172
|
+
static readonly NUM_WHITE_KEYS = 8
|
|
177
173
|
|
|
178
174
|
/**
|
|
179
175
|
* Height of a white piano key, in px.
|
|
180
176
|
*/
|
|
181
|
-
static readonly WHITE_KEY_HEIGHT = 72
|
|
177
|
+
static readonly WHITE_KEY_HEIGHT = 72
|
|
182
178
|
|
|
183
179
|
/**
|
|
184
180
|
* Width of a white piano key, in px.
|
|
185
181
|
*/
|
|
186
|
-
static readonly WHITE_KEY_WIDTH = 40
|
|
182
|
+
static readonly WHITE_KEY_WIDTH = 40
|
|
187
183
|
|
|
188
184
|
/**
|
|
189
185
|
* Height of a black piano key, in px.
|
|
190
186
|
*/
|
|
191
|
-
static readonly BLACK_KEY_HEIGHT = 40
|
|
187
|
+
static readonly BLACK_KEY_HEIGHT = 40
|
|
192
188
|
|
|
193
189
|
/**
|
|
194
190
|
* Width of a black piano key, in px.
|
|
195
191
|
*/
|
|
196
|
-
static readonly BLACK_KEY_WIDTH = 32
|
|
192
|
+
static readonly BLACK_KEY_WIDTH = 32
|
|
197
193
|
|
|
198
194
|
/**
|
|
199
195
|
* Radius of the curved bottom corner of a piano key, in px.
|
|
200
196
|
*/
|
|
201
|
-
static readonly KEY_RADIUS = 6
|
|
197
|
+
static readonly KEY_RADIUS = 6
|
|
202
198
|
|
|
203
199
|
/**
|
|
204
200
|
* Bottom padding for the labels on C keys.
|
|
205
201
|
*/
|
|
206
|
-
static readonly KEY_LABEL_PADDING = 8
|
|
202
|
+
static readonly KEY_LABEL_PADDING = 8
|
|
207
203
|
|
|
208
204
|
/**
|
|
209
205
|
* An array of objects with data describing the keys on the piano.
|
|
210
206
|
*/
|
|
211
207
|
static readonly KEY_INFO = [
|
|
212
|
-
{ name:
|
|
213
|
-
{ name:
|
|
214
|
-
{ name:
|
|
215
|
-
{ name:
|
|
216
|
-
{ name:
|
|
217
|
-
{ name:
|
|
218
|
-
{ name:
|
|
219
|
-
{ name:
|
|
220
|
-
{ name:
|
|
221
|
-
{ name:
|
|
222
|
-
{ name:
|
|
223
|
-
{ name:
|
|
224
|
-
{ name:
|
|
225
|
-
]
|
|
208
|
+
{ name: 'C', pitch: 0 },
|
|
209
|
+
{ name: 'C♯', pitch: 1, isBlack: true },
|
|
210
|
+
{ name: 'D', pitch: 2 },
|
|
211
|
+
{ name: 'E♭', pitch: 3, isBlack: true },
|
|
212
|
+
{ name: 'E', pitch: 4 },
|
|
213
|
+
{ name: 'F', pitch: 5 },
|
|
214
|
+
{ name: 'F♯', pitch: 6, isBlack: true },
|
|
215
|
+
{ name: 'G', pitch: 7 },
|
|
216
|
+
{ name: 'G♯', pitch: 8, isBlack: true },
|
|
217
|
+
{ name: 'A', pitch: 9 },
|
|
218
|
+
{ name: 'B♭', pitch: 10, isBlack: true },
|
|
219
|
+
{ name: 'B', pitch: 11 },
|
|
220
|
+
{ name: 'C', pitch: 12 },
|
|
221
|
+
]
|
|
226
222
|
|
|
227
223
|
/**
|
|
228
224
|
* The MIDI note number of the highest note selectable on the piano.
|
|
229
225
|
*/
|
|
230
|
-
static readonly MAX_NOTE = 130
|
|
226
|
+
static readonly MAX_NOTE = 130
|
|
231
227
|
|
|
232
228
|
/**
|
|
233
229
|
* The fraction of the distance to the target location to move the piano at each
|
|
234
230
|
* step of the animation.
|
|
235
231
|
*/
|
|
236
|
-
static readonly ANIMATION_FRACTION = 0.2
|
|
232
|
+
static readonly ANIMATION_FRACTION = 0.2
|
|
237
233
|
|
|
238
234
|
/**
|
|
239
235
|
* Path to the arrow svg icon, used on the octave buttons.
|
|
240
236
|
*/
|
|
241
|
-
static readonly ARROW_SVG_PATH =
|
|
237
|
+
static readonly ARROW_SVG_PATH = 'icons/arrow_button.svg'
|
|
242
238
|
|
|
243
239
|
/**
|
|
244
240
|
* The size of the square octave buttons.
|
|
245
241
|
*/
|
|
246
|
-
static readonly OCTAVE_BUTTON_SIZE = 32
|
|
242
|
+
static readonly OCTAVE_BUTTON_SIZE = 32
|
|
247
243
|
|
|
248
244
|
/**
|
|
249
245
|
* Construct a FieldNote from a JSON arg object.
|
|
250
|
-
*
|
|
251
246
|
* @param options A JSON object with options.
|
|
252
247
|
* @returns The new field instance.
|
|
253
248
|
*/
|
|
254
249
|
static fromJson(options: FieldNoteJsonConfig): FieldNote {
|
|
255
|
-
return new FieldNote(options
|
|
250
|
+
return new FieldNote(options.note)
|
|
256
251
|
}
|
|
257
252
|
|
|
258
253
|
/**
|
|
259
254
|
* Clean up this FieldNote, as well as the inherited FieldTextInput.
|
|
260
255
|
*/
|
|
261
256
|
dispose() {
|
|
262
|
-
super.dispose()
|
|
263
|
-
this.mouseDownWrappers_.forEach(
|
|
264
|
-
Blockly.browserEvents.unbind(wrapper)
|
|
265
|
-
})
|
|
266
|
-
this.mouseEnterWrappers_.forEach(
|
|
267
|
-
Blockly.browserEvents.unbind(wrapper)
|
|
268
|
-
})
|
|
257
|
+
super.dispose()
|
|
258
|
+
this.mouseDownWrappers_.forEach(wrapper => {
|
|
259
|
+
Blockly.browserEvents.unbind(wrapper)
|
|
260
|
+
})
|
|
261
|
+
this.mouseEnterWrappers_.forEach(wrapper => {
|
|
262
|
+
Blockly.browserEvents.unbind(wrapper)
|
|
263
|
+
})
|
|
269
264
|
if (this.mouseUpWrapper_) {
|
|
270
|
-
Blockly.browserEvents.unbind(this.mouseUpWrapper_)
|
|
265
|
+
Blockly.browserEvents.unbind(this.mouseUpWrapper_)
|
|
271
266
|
}
|
|
272
267
|
if (this.octaveDownMouseDownWrapper_) {
|
|
273
|
-
Blockly.browserEvents.unbind(this.octaveDownMouseDownWrapper_)
|
|
268
|
+
Blockly.browserEvents.unbind(this.octaveDownMouseDownWrapper_)
|
|
274
269
|
}
|
|
275
270
|
if (this.octaveUpMouseDownWrapper_) {
|
|
276
|
-
Blockly.browserEvents.unbind(this.octaveUpMouseDownWrapper_)
|
|
271
|
+
Blockly.browserEvents.unbind(this.octaveUpMouseDownWrapper_)
|
|
277
272
|
}
|
|
278
|
-
this.pianoSVG_ = null
|
|
279
|
-
this.keySVGs_.length = 0
|
|
280
|
-
this.noteNameText_ = null
|
|
281
|
-
this.lowCText_ = null
|
|
282
|
-
this.highCText_ = null
|
|
273
|
+
this.pianoSVG_ = null
|
|
274
|
+
this.keySVGs_.length = 0
|
|
275
|
+
this.noteNameText_ = null
|
|
276
|
+
this.lowCText_ = null
|
|
277
|
+
this.highCText_ = null
|
|
283
278
|
}
|
|
284
279
|
|
|
285
280
|
/**
|
|
286
281
|
* Show a field with piano keys.
|
|
282
|
+
* @param event The triggering pointer event.
|
|
283
|
+
* @param quietInput If true, suppress the sound preview while the editor opens.
|
|
287
284
|
*/
|
|
288
285
|
showEditor_(event: PointerEvent, quietInput = false) {
|
|
289
|
-
super.showEditor_(event, quietInput, false)
|
|
286
|
+
super.showEditor_(event, quietInput, false)
|
|
290
287
|
|
|
291
288
|
// Build the SVG DOM.
|
|
292
|
-
const div = Blockly.DropDownDiv.getContentDiv()
|
|
289
|
+
const div = Blockly.DropDownDiv.getContentDiv()
|
|
293
290
|
|
|
294
|
-
this.fieldEditorWidth_ =
|
|
295
|
-
|
|
296
|
-
FieldNote.EDGE_PADDING;
|
|
297
|
-
this.fieldEditorHeight_ =
|
|
298
|
-
FieldNote.TOP_MENU_HEIGHT +
|
|
299
|
-
FieldNote.WHITE_KEY_HEIGHT +
|
|
300
|
-
FieldNote.EDGE_PADDING;
|
|
291
|
+
this.fieldEditorWidth_ = FieldNote.NUM_WHITE_KEYS * FieldNote.WHITE_KEY_WIDTH + FieldNote.EDGE_PADDING
|
|
292
|
+
this.fieldEditorHeight_ = FieldNote.TOP_MENU_HEIGHT + FieldNote.WHITE_KEY_HEIGHT + FieldNote.EDGE_PADDING
|
|
301
293
|
|
|
302
294
|
const svg = Blockly.utils.dom.createSvgElement(
|
|
303
|
-
|
|
295
|
+
'svg',
|
|
304
296
|
{
|
|
305
|
-
xmlns:
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
version:
|
|
309
|
-
height: this.fieldEditorHeight_ +
|
|
310
|
-
width: this.fieldEditorWidth_ +
|
|
297
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
298
|
+
'xmlns:html': 'http://www.w3.org/1999/xhtml',
|
|
299
|
+
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
|
300
|
+
version: '1.1',
|
|
301
|
+
height: this.fieldEditorHeight_ + 'px',
|
|
302
|
+
width: this.fieldEditorWidth_ + 'px',
|
|
311
303
|
},
|
|
312
|
-
div
|
|
313
|
-
)
|
|
304
|
+
div,
|
|
305
|
+
)
|
|
314
306
|
|
|
315
307
|
// Add the white and black keys
|
|
316
308
|
// Since we are adding the keys from left to right in order, they need
|
|
317
309
|
// to be in two groups in order to layer correctly.
|
|
318
|
-
this.pianoSVG_ = Blockly.utils.dom.createSvgElement(
|
|
319
|
-
const whiteKeyGroup = Blockly.utils.dom.createSvgElement(
|
|
320
|
-
|
|
321
|
-
{},
|
|
322
|
-
this.pianoSVG_
|
|
323
|
-
);
|
|
324
|
-
const blackKeyGroup = Blockly.utils.dom.createSvgElement(
|
|
325
|
-
"g",
|
|
326
|
-
{},
|
|
327
|
-
this.pianoSVG_
|
|
328
|
-
);
|
|
310
|
+
this.pianoSVG_ = Blockly.utils.dom.createSvgElement('g', {}, svg)
|
|
311
|
+
const whiteKeyGroup = Blockly.utils.dom.createSvgElement('g', {}, this.pianoSVG_)
|
|
312
|
+
const blackKeyGroup = Blockly.utils.dom.createSvgElement('g', {}, this.pianoSVG_)
|
|
329
313
|
|
|
330
314
|
// Add three piano octaves, so we can animate moving up or down an octave.
|
|
331
315
|
// Only the middle octave gets bound to events.
|
|
332
|
-
this.keySVGs_ = []
|
|
333
|
-
this.addPianoOctave_(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
blackKeyGroup,
|
|
337
|
-
null
|
|
338
|
-
);
|
|
339
|
-
this.addPianoOctave_(0, whiteKeyGroup, blackKeyGroup, this.keySVGs_);
|
|
340
|
-
this.addPianoOctave_(
|
|
341
|
-
this.fieldEditorWidth_ - FieldNote.EDGE_PADDING,
|
|
342
|
-
whiteKeyGroup,
|
|
343
|
-
blackKeyGroup,
|
|
344
|
-
null
|
|
345
|
-
);
|
|
316
|
+
this.keySVGs_ = []
|
|
317
|
+
this.addPianoOctave_(-this.fieldEditorWidth_ + FieldNote.EDGE_PADDING, whiteKeyGroup, blackKeyGroup, null)
|
|
318
|
+
this.addPianoOctave_(0, whiteKeyGroup, blackKeyGroup, this.keySVGs_)
|
|
319
|
+
this.addPianoOctave_(this.fieldEditorWidth_ - FieldNote.EDGE_PADDING, whiteKeyGroup, blackKeyGroup, null)
|
|
346
320
|
|
|
347
321
|
// Note name indicator at the top of the field
|
|
348
322
|
this.noteNameText_ = Blockly.utils.dom.createSvgElement(
|
|
349
|
-
|
|
323
|
+
'text',
|
|
350
324
|
{
|
|
351
325
|
x: this.fieldEditorWidth_ / 2,
|
|
352
326
|
y: FieldNote.TOP_MENU_HEIGHT / 2,
|
|
353
|
-
class:
|
|
354
|
-
|
|
355
|
-
|
|
327
|
+
class: 'blocklyText',
|
|
328
|
+
'text-anchor': 'middle',
|
|
329
|
+
'dominant-baseline': 'middle',
|
|
356
330
|
},
|
|
357
|
-
svg
|
|
358
|
-
)
|
|
331
|
+
svg,
|
|
332
|
+
)
|
|
359
333
|
|
|
360
334
|
// Note names on the low and high C keys
|
|
361
|
-
const lowCX = FieldNote.WHITE_KEY_WIDTH / 2
|
|
362
|
-
this.lowCText_ = this.addCKeyLabel_(lowCX, svg)
|
|
363
|
-
const highCX =
|
|
364
|
-
|
|
365
|
-
this.highCText_ = this.addCKeyLabel_(highCX, svg);
|
|
335
|
+
const lowCX = FieldNote.WHITE_KEY_WIDTH / 2
|
|
336
|
+
this.lowCText_ = this.addCKeyLabel_(lowCX, svg)
|
|
337
|
+
const highCX = lowCX + FieldNote.WHITE_KEY_WIDTH * (FieldNote.NUM_WHITE_KEYS - 1)
|
|
338
|
+
this.highCText_ = this.addCKeyLabel_(highCX, svg)
|
|
366
339
|
|
|
367
340
|
// Horizontal line at the top of the keys
|
|
368
341
|
Blockly.utils.dom.createSvgElement(
|
|
369
|
-
|
|
342
|
+
'line',
|
|
370
343
|
{
|
|
371
|
-
stroke: (
|
|
372
|
-
this.sourceBlock_.getParent() as Blockly.BlockSvg
|
|
373
|
-
).getColourTertiary(),
|
|
344
|
+
stroke: (this.sourceBlock_!.getParent() as Blockly.BlockSvg).getColourTertiary(),
|
|
374
345
|
x1: 0,
|
|
375
346
|
y1: FieldNote.TOP_MENU_HEIGHT,
|
|
376
347
|
x2: this.fieldEditorWidth_,
|
|
377
348
|
y2: FieldNote.TOP_MENU_HEIGHT,
|
|
378
349
|
},
|
|
379
|
-
svg
|
|
380
|
-
)
|
|
350
|
+
svg,
|
|
351
|
+
)
|
|
381
352
|
|
|
382
353
|
// Drop shadow at the top of the keys
|
|
383
354
|
Blockly.utils.dom.createSvgElement(
|
|
384
|
-
|
|
355
|
+
'rect',
|
|
385
356
|
{
|
|
386
357
|
x: 0,
|
|
387
358
|
y: FieldNote.TOP_MENU_HEIGHT,
|
|
388
359
|
width: this.fieldEditorWidth_,
|
|
389
360
|
height: FieldNote.SHADOW_HEIGHT,
|
|
390
361
|
fill: FieldNote.SHADOW_COLOR,
|
|
391
|
-
|
|
362
|
+
'fill-opacity': FieldNote.SHADOW_OPACITY,
|
|
392
363
|
},
|
|
393
|
-
svg
|
|
394
|
-
)
|
|
364
|
+
svg,
|
|
365
|
+
)
|
|
395
366
|
|
|
396
367
|
// Octave buttons
|
|
397
|
-
const octaveDownButton = this.addOctaveButton_(0, true, svg)
|
|
368
|
+
const octaveDownButton = this.addOctaveButton_(0, true, svg)
|
|
398
369
|
const octaveUpButton = this.addOctaveButton_(
|
|
399
|
-
this.fieldEditorWidth_ +
|
|
400
|
-
FieldNote.INSET * 2 -
|
|
401
|
-
FieldNote.OCTAVE_BUTTON_SIZE,
|
|
370
|
+
this.fieldEditorWidth_ + FieldNote.INSET * 2 - FieldNote.OCTAVE_BUTTON_SIZE,
|
|
402
371
|
false,
|
|
403
|
-
svg
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
this.octaveDownMouseDownWrapper_ = Blockly.browserEvents.bind(
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
this,
|
|
418
|
-
function () {
|
|
419
|
-
this.changeOctaveBy_(1);
|
|
420
|
-
}
|
|
421
|
-
);
|
|
422
|
-
const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg;
|
|
423
|
-
Blockly.DropDownDiv.setColour(
|
|
424
|
-
sourceBlock.getParent().getColour(),
|
|
425
|
-
sourceBlock.getParent().getColourTertiary()
|
|
426
|
-
);
|
|
427
|
-
Blockly.DropDownDiv.showPositionedByBlock(this, sourceBlock);
|
|
428
|
-
|
|
429
|
-
this.updateSelection_();
|
|
372
|
+
svg,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
this.octaveDownMouseDownWrapper_ = Blockly.browserEvents.bind(octaveDownButton, 'mousedown', this, () => {
|
|
376
|
+
this.changeOctaveBy_(-1)
|
|
377
|
+
})
|
|
378
|
+
this.octaveUpMouseDownWrapper_ = Blockly.browserEvents.bind(octaveUpButton, 'mousedown', this, () => {
|
|
379
|
+
this.changeOctaveBy_(1)
|
|
380
|
+
})
|
|
381
|
+
const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg
|
|
382
|
+
Blockly.DropDownDiv.setColour(sourceBlock.getParent()!.getColour(), sourceBlock.getParent()!.getColourTertiary())
|
|
383
|
+
Blockly.DropDownDiv.showPositionedByBlock(this, sourceBlock)
|
|
384
|
+
|
|
385
|
+
this.updateSelection_()
|
|
430
386
|
}
|
|
431
387
|
|
|
432
388
|
/**
|
|
433
389
|
* Add one octave of piano keys drawn using SVG.
|
|
434
|
-
*
|
|
435
390
|
* @param x The x position of the left edge of this octave of keys.
|
|
436
391
|
* @param whiteKeyGroup The group for all white piano keys.
|
|
437
392
|
* @param blackKeyGroup The group for all black piano keys.
|
|
@@ -441,59 +396,47 @@ export class FieldNote extends Blockly.FieldTextInput {
|
|
|
441
396
|
x: number,
|
|
442
397
|
whiteKeyGroup: SVGElement,
|
|
443
398
|
blackKeyGroup: SVGElement,
|
|
444
|
-
keySVGarray: SVGElement[]
|
|
399
|
+
keySVGarray: SVGElement[] | null,
|
|
445
400
|
) {
|
|
446
|
-
let xIncrement, width, height, fill, stroke, group
|
|
447
|
-
x += FieldNote.EDGE_PADDING / 2
|
|
448
|
-
const y = FieldNote.TOP_MENU_HEIGHT
|
|
449
|
-
for (
|
|
401
|
+
let xIncrement, width, height, fill, stroke, group
|
|
402
|
+
x += FieldNote.EDGE_PADDING / 2
|
|
403
|
+
const y = FieldNote.TOP_MENU_HEIGHT
|
|
404
|
+
for (let i = 0; i < FieldNote.KEY_INFO.length; i++) {
|
|
450
405
|
// Draw a black or white key
|
|
451
406
|
if (FieldNote.KEY_INFO[i].isBlack) {
|
|
452
407
|
// Black keys are shifted back half a key
|
|
453
|
-
x -= FieldNote.BLACK_KEY_WIDTH / 2
|
|
454
|
-
xIncrement = FieldNote.BLACK_KEY_WIDTH / 2
|
|
455
|
-
width = FieldNote.BLACK_KEY_WIDTH
|
|
456
|
-
height = FieldNote.BLACK_KEY_HEIGHT
|
|
457
|
-
fill = FieldNote.BLACK_KEY_COLOR
|
|
458
|
-
stroke = FieldNote.BLACK_KEY_STROKE
|
|
459
|
-
group = blackKeyGroup
|
|
408
|
+
x -= FieldNote.BLACK_KEY_WIDTH / 2
|
|
409
|
+
xIncrement = FieldNote.BLACK_KEY_WIDTH / 2
|
|
410
|
+
width = FieldNote.BLACK_KEY_WIDTH
|
|
411
|
+
height = FieldNote.BLACK_KEY_HEIGHT
|
|
412
|
+
fill = FieldNote.BLACK_KEY_COLOR
|
|
413
|
+
stroke = FieldNote.BLACK_KEY_STROKE
|
|
414
|
+
group = blackKeyGroup
|
|
460
415
|
} else {
|
|
461
|
-
xIncrement = FieldNote.WHITE_KEY_WIDTH
|
|
462
|
-
width = FieldNote.WHITE_KEY_WIDTH
|
|
463
|
-
height = FieldNote.WHITE_KEY_HEIGHT
|
|
464
|
-
fill = FieldNote.WHITE_KEY_COLOR
|
|
465
|
-
stroke = (
|
|
466
|
-
|
|
467
|
-
).getColourTertiary();
|
|
468
|
-
group = whiteKeyGroup;
|
|
416
|
+
xIncrement = FieldNote.WHITE_KEY_WIDTH
|
|
417
|
+
width = FieldNote.WHITE_KEY_WIDTH
|
|
418
|
+
height = FieldNote.WHITE_KEY_HEIGHT
|
|
419
|
+
fill = FieldNote.WHITE_KEY_COLOR
|
|
420
|
+
stroke = (this.sourceBlock_!.getParent() as Blockly.BlockSvg).getColourTertiary()
|
|
421
|
+
group = whiteKeyGroup
|
|
469
422
|
}
|
|
470
423
|
const attr = {
|
|
471
424
|
d: this.getPianoKeyPath_(x, y, width, height),
|
|
472
425
|
fill: fill,
|
|
473
426
|
stroke: stroke,
|
|
474
|
-
}
|
|
475
|
-
x += xIncrement
|
|
427
|
+
}
|
|
428
|
+
x += xIncrement
|
|
476
429
|
|
|
477
|
-
const keySVG = Blockly.utils.dom.createSvgElement(
|
|
430
|
+
const keySVG = Blockly.utils.dom.createSvgElement('path', attr, group)
|
|
478
431
|
|
|
479
432
|
if (keySVGarray) {
|
|
480
|
-
keySVGarray[i] = keySVG
|
|
481
|
-
keySVG.setAttribute(
|
|
482
|
-
keySVG.setAttribute(
|
|
483
|
-
keySVG.setAttribute(
|
|
484
|
-
|
|
485
|
-
this.mouseDownWrappers_[i] = Blockly.browserEvents.bind(
|
|
486
|
-
|
|
487
|
-
"mousedown",
|
|
488
|
-
this,
|
|
489
|
-
this.onMouseDownOnKey_
|
|
490
|
-
);
|
|
491
|
-
this.mouseEnterWrappers_[i] = Blockly.browserEvents.bind(
|
|
492
|
-
keySVG,
|
|
493
|
-
"mouseenter",
|
|
494
|
-
this,
|
|
495
|
-
this.onMouseEnter_
|
|
496
|
-
);
|
|
433
|
+
keySVGarray[i] = keySVG
|
|
434
|
+
keySVG.setAttribute('data-pitch', `${FieldNote.KEY_INFO[i].pitch}`)
|
|
435
|
+
keySVG.setAttribute('data-name', `${FieldNote.KEY_INFO[i].name}`)
|
|
436
|
+
keySVG.setAttribute('data-isBlack', `${FieldNote.KEY_INFO[i].isBlack}`)
|
|
437
|
+
|
|
438
|
+
this.mouseDownWrappers_[i] = Blockly.browserEvents.bind(keySVG, 'mousedown', this, this.onMouseDownOnKey_)
|
|
439
|
+
this.mouseEnterWrappers_[i] = Blockly.browserEvents.bind(keySVG, 'mouseenter', this, this.onMouseEnter_)
|
|
497
440
|
}
|
|
498
441
|
}
|
|
499
442
|
}
|
|
@@ -501,217 +444,184 @@ export class FieldNote extends Blockly.FieldTextInput {
|
|
|
501
444
|
/**
|
|
502
445
|
* Construct the SVG path string for a piano key shape: a rectangle with rounded
|
|
503
446
|
* corners at the bottom.
|
|
504
|
-
*
|
|
505
447
|
* @param x the x position for the key.
|
|
506
448
|
* @param y the y position for the key.
|
|
507
|
-
* @param width the
|
|
508
|
-
* @param height the
|
|
449
|
+
* @param width the horizontal extent of the key in pixels.
|
|
450
|
+
* @param height the vertical extent of the key in pixels.
|
|
509
451
|
* @returns the SVG path as a string.
|
|
510
452
|
*/
|
|
511
|
-
private getPianoKeyPath_(
|
|
512
|
-
x: number,
|
|
513
|
-
y: number,
|
|
514
|
-
width: number,
|
|
515
|
-
height: number
|
|
516
|
-
): string {
|
|
453
|
+
private getPianoKeyPath_(x: number, y: number, width: number, height: number): string {
|
|
517
454
|
return (
|
|
518
|
-
|
|
455
|
+
'M' +
|
|
519
456
|
x +
|
|
520
|
-
|
|
457
|
+
' ' +
|
|
521
458
|
y +
|
|
522
|
-
|
|
523
|
-
|
|
459
|
+
' ' +
|
|
460
|
+
'L' +
|
|
524
461
|
x +
|
|
525
|
-
|
|
462
|
+
' ' +
|
|
526
463
|
(y + height - FieldNote.KEY_RADIUS) +
|
|
527
|
-
|
|
528
|
-
|
|
464
|
+
' ' +
|
|
465
|
+
'Q' +
|
|
529
466
|
x +
|
|
530
|
-
|
|
467
|
+
' ' +
|
|
531
468
|
(y + height) +
|
|
532
|
-
|
|
469
|
+
' ' +
|
|
533
470
|
(x + FieldNote.KEY_RADIUS) +
|
|
534
|
-
|
|
471
|
+
' ' +
|
|
535
472
|
(y + height) +
|
|
536
|
-
|
|
537
|
-
|
|
473
|
+
' ' +
|
|
474
|
+
'L' +
|
|
538
475
|
(x + width - FieldNote.KEY_RADIUS) +
|
|
539
|
-
|
|
476
|
+
' ' +
|
|
540
477
|
(y + height) +
|
|
541
|
-
|
|
542
|
-
|
|
478
|
+
' ' +
|
|
479
|
+
'Q' +
|
|
543
480
|
(x + width) +
|
|
544
|
-
|
|
481
|
+
' ' +
|
|
545
482
|
(y + height) +
|
|
546
|
-
|
|
483
|
+
' ' +
|
|
547
484
|
(x + width) +
|
|
548
|
-
|
|
485
|
+
' ' +
|
|
549
486
|
(y + height - FieldNote.KEY_RADIUS) +
|
|
550
|
-
|
|
551
|
-
|
|
487
|
+
' ' +
|
|
488
|
+
'L' +
|
|
552
489
|
(x + width) +
|
|
553
|
-
|
|
490
|
+
' ' +
|
|
554
491
|
y +
|
|
555
|
-
|
|
556
|
-
|
|
492
|
+
' ' +
|
|
493
|
+
'L' +
|
|
557
494
|
x +
|
|
558
|
-
|
|
495
|
+
' ' +
|
|
559
496
|
y
|
|
560
|
-
)
|
|
497
|
+
)
|
|
561
498
|
}
|
|
562
499
|
|
|
563
500
|
/**
|
|
564
501
|
* Add a button for switching the displayed octave of the piano up or down.
|
|
565
|
-
*
|
|
566
502
|
* @param x The x position of the button.
|
|
567
503
|
* @param flipped If true, the icon should be flipped.
|
|
568
504
|
* @param svg The svg element to add the buttons to.
|
|
569
505
|
* @returns A group containing the button SVG elements.
|
|
570
506
|
*/
|
|
571
|
-
private addOctaveButton_(
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
svg: SVGElement
|
|
575
|
-
): SVGElement {
|
|
576
|
-
const group = Blockly.utils.dom.createSvgElement("g", {}, svg);
|
|
577
|
-
const imageSize = FieldNote.OCTAVE_BUTTON_SIZE;
|
|
507
|
+
private addOctaveButton_(x: number, flipped: boolean, svg: SVGElement): SVGElement {
|
|
508
|
+
const group = Blockly.utils.dom.createSvgElement('g', {}, svg)
|
|
509
|
+
const imageSize = FieldNote.OCTAVE_BUTTON_SIZE
|
|
578
510
|
const arrow = Blockly.utils.dom.createSvgElement(
|
|
579
|
-
|
|
511
|
+
'image',
|
|
580
512
|
{
|
|
581
513
|
width: imageSize,
|
|
582
514
|
height: imageSize,
|
|
583
515
|
x: x - FieldNote.INSET,
|
|
584
516
|
y: -1 * FieldNote.INSET,
|
|
585
517
|
},
|
|
586
|
-
group
|
|
587
|
-
)
|
|
518
|
+
group,
|
|
519
|
+
)
|
|
588
520
|
arrow.setAttributeNS(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
Blockly.getMainWorkspace().options.pathToMedia + FieldNote.ARROW_SVG_PATH
|
|
592
|
-
)
|
|
521
|
+
'http://www.w3.org/1999/xlink',
|
|
522
|
+
'xlink:href',
|
|
523
|
+
Blockly.getMainWorkspace().options.pathToMedia + FieldNote.ARROW_SVG_PATH,
|
|
524
|
+
)
|
|
593
525
|
Blockly.utils.dom.createSvgElement(
|
|
594
|
-
|
|
526
|
+
'line',
|
|
595
527
|
{
|
|
596
|
-
stroke: (
|
|
597
|
-
this.sourceBlock_.getParent() as Blockly.BlockSvg
|
|
598
|
-
).getColourTertiary(),
|
|
528
|
+
stroke: (this.sourceBlock_!.getParent() as Blockly.BlockSvg).getColourTertiary(),
|
|
599
529
|
x1: x - FieldNote.INSET,
|
|
600
530
|
y1: 0,
|
|
601
531
|
x2: x - FieldNote.INSET,
|
|
602
532
|
y2: FieldNote.TOP_MENU_HEIGHT - FieldNote.INSET,
|
|
603
533
|
},
|
|
604
|
-
group
|
|
605
|
-
)
|
|
534
|
+
group,
|
|
535
|
+
)
|
|
606
536
|
if (flipped) {
|
|
607
|
-
const translateX =
|
|
608
|
-
|
|
609
|
-
group.setAttribute(
|
|
610
|
-
"transform",
|
|
611
|
-
"scale(-1, 1) " + "translate(" + translateX + ", 0)"
|
|
612
|
-
);
|
|
537
|
+
const translateX = -1 * FieldNote.OCTAVE_BUTTON_SIZE + FieldNote.INSET * 2
|
|
538
|
+
group.setAttribute('transform', 'scale(-1, 1) ' + 'translate(' + translateX + ', 0)')
|
|
613
539
|
}
|
|
614
|
-
return group
|
|
540
|
+
return group
|
|
615
541
|
}
|
|
616
542
|
|
|
617
543
|
/**
|
|
618
544
|
* Add an SVG text label for display on the C keys of the piano.
|
|
619
|
-
*
|
|
620
545
|
* @param x The x position for the label.
|
|
621
546
|
* @param svg The SVG element to add the label to.
|
|
622
547
|
* @returns The SVG element containing the label.
|
|
623
548
|
*/
|
|
624
549
|
private addCKeyLabel_(x: number, svg: SVGElement): SVGElement {
|
|
625
550
|
return Blockly.utils.dom.createSvgElement(
|
|
626
|
-
|
|
551
|
+
'text',
|
|
627
552
|
{
|
|
628
553
|
x: x,
|
|
629
|
-
y:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
FieldNote.KEY_LABEL_PADDING,
|
|
633
|
-
class: "scratchNotePickerKeyLabel",
|
|
634
|
-
"text-anchor": "middle",
|
|
554
|
+
y: FieldNote.TOP_MENU_HEIGHT + FieldNote.WHITE_KEY_HEIGHT - FieldNote.KEY_LABEL_PADDING,
|
|
555
|
+
class: 'scratchNotePickerKeyLabel',
|
|
556
|
+
'text-anchor': 'middle',
|
|
635
557
|
},
|
|
636
|
-
svg
|
|
637
|
-
)
|
|
558
|
+
svg,
|
|
559
|
+
)
|
|
638
560
|
}
|
|
639
561
|
|
|
640
562
|
/**
|
|
641
563
|
* Set the visibility of the C key labels.
|
|
642
|
-
*
|
|
643
564
|
* @param visible If true, set labels to be visible.
|
|
644
565
|
*/
|
|
645
566
|
private setCKeyLabelsVisible_(visible: boolean) {
|
|
646
|
-
if (
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
} else {
|
|
650
|
-
this.fadeSvgToOpacity_(this.lowCText_, 0);
|
|
651
|
-
this.fadeSvgToOpacity_(this.highCText_, 0);
|
|
567
|
+
if (!this.lowCText_ || !this.highCText_) {
|
|
568
|
+
console.warn('FieldNote.setCKeyLabelsVisible_: C-key label DOM is not fully initialized')
|
|
569
|
+
return
|
|
652
570
|
}
|
|
571
|
+
const opacity = visible ? 1 : 0
|
|
572
|
+
this.fadeSvgToOpacity_(this.lowCText_, opacity)
|
|
573
|
+
this.fadeSvgToOpacity_(this.highCText_, opacity)
|
|
653
574
|
}
|
|
654
575
|
|
|
655
576
|
/**
|
|
656
577
|
* Animate an SVG to fade it in or out to a target opacity.
|
|
657
|
-
*
|
|
658
578
|
* @param svg The SVG element to apply the fade to.
|
|
659
579
|
* @param opacity The target opacity.
|
|
660
580
|
*/
|
|
661
581
|
private fadeSvgToOpacity_(svg: SVGElement, opacity: number) {
|
|
662
|
-
svg.setAttribute(
|
|
663
|
-
"style",
|
|
664
|
-
"opacity: " + opacity + "; transition: opacity 0.1s;"
|
|
665
|
-
);
|
|
582
|
+
svg.setAttribute('style', 'opacity: ' + opacity + '; transition: opacity 0.1s;')
|
|
666
583
|
}
|
|
667
584
|
|
|
668
585
|
/**
|
|
669
586
|
* Handle the mouse down event on a piano key.
|
|
670
|
-
*
|
|
671
587
|
* @param e Mouse down event.
|
|
672
588
|
*/
|
|
673
589
|
private onMouseDownOnKey_(e: PointerEvent) {
|
|
674
|
-
this.mouseIsDown_ = true
|
|
675
|
-
this.mouseUpWrapper_ = Blockly.browserEvents.bind(
|
|
676
|
-
|
|
677
|
-
"mouseup",
|
|
678
|
-
this,
|
|
679
|
-
this.onMouseUp_
|
|
680
|
-
);
|
|
681
|
-
this.selectNoteWithMouseEvent_(e);
|
|
590
|
+
this.mouseIsDown_ = true
|
|
591
|
+
this.mouseUpWrapper_ = Blockly.browserEvents.bind(document.body, 'mouseup', this, this.onMouseUp_)
|
|
592
|
+
this.selectNoteWithMouseEvent_(e)
|
|
682
593
|
}
|
|
683
594
|
|
|
684
595
|
/**
|
|
685
596
|
* Handle the mouse up event following a mouse down on a piano key.
|
|
686
597
|
*/
|
|
687
598
|
private onMouseUp_() {
|
|
688
|
-
this.mouseIsDown_ = false
|
|
689
|
-
|
|
690
|
-
|
|
599
|
+
this.mouseIsDown_ = false
|
|
600
|
+
if (this.mouseUpWrapper_) {
|
|
601
|
+
Blockly.browserEvents.unbind(this.mouseUpWrapper_)
|
|
602
|
+
this.mouseUpWrapper_ = null
|
|
603
|
+
}
|
|
691
604
|
}
|
|
692
605
|
|
|
693
606
|
/**
|
|
694
607
|
* Handle the event when the mouse enters a piano key.
|
|
695
|
-
*
|
|
696
608
|
* @param e Mouse enter event.
|
|
697
609
|
*/
|
|
698
610
|
private onMouseEnter_(e: PointerEvent) {
|
|
699
611
|
if (this.mouseIsDown_) {
|
|
700
|
-
this.selectNoteWithMouseEvent_(e)
|
|
612
|
+
this.selectNoteWithMouseEvent_(e)
|
|
701
613
|
}
|
|
702
614
|
}
|
|
703
615
|
|
|
704
616
|
/**
|
|
705
617
|
* Use the data in a mouse event to select a new note, and play it.
|
|
706
|
-
*
|
|
707
618
|
* @param e Mouse event.
|
|
708
619
|
*/
|
|
709
620
|
private selectNoteWithMouseEvent_(e: PointerEvent) {
|
|
710
621
|
const newNoteNum =
|
|
711
|
-
Number((e.target as HTMLElement).getAttribute(
|
|
712
|
-
|
|
713
|
-
this.
|
|
714
|
-
this.playNoteInternal_();
|
|
622
|
+
Number((e.target as HTMLElement).getAttribute('data-pitch')) + (this.displayedOctave_ ?? 0) * 12
|
|
623
|
+
this.setEditorValue_(newNoteNum)
|
|
624
|
+
this.playNoteInternal_()
|
|
715
625
|
}
|
|
716
626
|
|
|
717
627
|
/**
|
|
@@ -719,94 +629,86 @@ export class FieldNote extends Blockly.FieldTextInput {
|
|
|
719
629
|
*/
|
|
720
630
|
private playNoteInternal_() {
|
|
721
631
|
if (FieldNote.playNote_) {
|
|
722
|
-
FieldNote.playNote_(Number(this.getValue()),
|
|
632
|
+
FieldNote.playNote_(Number(this.getValue()!), 'Music')
|
|
723
633
|
}
|
|
724
634
|
}
|
|
725
635
|
|
|
726
636
|
/**
|
|
727
637
|
* Function to play a musical note corresponding to the key selected.
|
|
728
638
|
* Overridden externally.
|
|
729
|
-
*
|
|
730
639
|
* @param noteNum the MIDI note number to play.
|
|
731
640
|
* @param id An id to select a scratch extension to play the note.
|
|
732
641
|
*/
|
|
733
642
|
static playNote_ = function (noteNum: number, id: string) {
|
|
734
|
-
return
|
|
735
|
-
}
|
|
643
|
+
return
|
|
644
|
+
}
|
|
736
645
|
|
|
737
646
|
/**
|
|
738
647
|
* Change the selected note by a number of octaves, and start the animation.
|
|
739
|
-
*
|
|
740
648
|
* @param octaves The number of octaves to change by.
|
|
741
649
|
*/
|
|
742
650
|
private changeOctaveBy_(octaves: number) {
|
|
743
|
-
this.displayedOctave_
|
|
651
|
+
this.displayedOctave_ = (this.displayedOctave_ ?? 0) + octaves
|
|
744
652
|
if (this.displayedOctave_ < 0) {
|
|
745
|
-
this.displayedOctave_ = 0
|
|
746
|
-
return
|
|
653
|
+
this.displayedOctave_ = 0
|
|
654
|
+
return
|
|
747
655
|
}
|
|
748
|
-
const maxOctave = Math.floor(FieldNote.MAX_NOTE / 12)
|
|
656
|
+
const maxOctave = Math.floor(FieldNote.MAX_NOTE / 12)
|
|
749
657
|
if (this.displayedOctave_ > maxOctave) {
|
|
750
|
-
this.displayedOctave_ = maxOctave
|
|
751
|
-
return
|
|
658
|
+
this.displayedOctave_ = maxOctave
|
|
659
|
+
return
|
|
752
660
|
}
|
|
753
661
|
|
|
754
|
-
const newNote = Number(this.getText()) + octaves * 12
|
|
755
|
-
this.setEditorValue_(newNote)
|
|
662
|
+
const newNote = Number(this.getText()) + octaves * 12
|
|
663
|
+
this.setEditorValue_(newNote)
|
|
756
664
|
|
|
757
|
-
this.animationTarget_ = this.fieldEditorWidth_ * octaves * -1
|
|
758
|
-
this.animationPos_ = 0
|
|
759
|
-
this.stepOctaveAnimation_()
|
|
760
|
-
this.setCKeyLabelsVisible_(false)
|
|
665
|
+
this.animationTarget_ = this.fieldEditorWidth_ * octaves * -1
|
|
666
|
+
this.animationPos_ = 0
|
|
667
|
+
this.stepOctaveAnimation_()
|
|
668
|
+
this.setCKeyLabelsVisible_(false)
|
|
761
669
|
}
|
|
762
670
|
|
|
763
671
|
/**
|
|
764
672
|
* Animate the piano up or down an octave by sliding it to the left or right.
|
|
765
673
|
*/
|
|
766
674
|
private stepOctaveAnimation_() {
|
|
767
|
-
const absDiff = Math.abs(this.animationPos_ - this.animationTarget_)
|
|
675
|
+
const absDiff = Math.abs(this.animationPos_ - this.animationTarget_)
|
|
768
676
|
if (absDiff < 1) {
|
|
769
|
-
this.pianoSVG_
|
|
770
|
-
this.setCKeyLabelsVisible_(true)
|
|
771
|
-
this.playNoteInternal_()
|
|
772
|
-
return
|
|
677
|
+
this.pianoSVG_?.setAttribute('transform', 'translate(0, 0)')
|
|
678
|
+
this.setCKeyLabelsVisible_(true)
|
|
679
|
+
this.playNoteInternal_()
|
|
680
|
+
return
|
|
773
681
|
}
|
|
774
|
-
this.animationPos_ +=
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
this.pianoSVG_.setAttribute(
|
|
778
|
-
"transform",
|
|
779
|
-
"translate(" + this.animationPos_ + ",0)"
|
|
780
|
-
);
|
|
781
|
-
requestAnimationFrame(this.stepOctaveAnimation_.bind(this));
|
|
682
|
+
this.animationPos_ += (this.animationTarget_ - this.animationPos_) * FieldNote.ANIMATION_FRACTION
|
|
683
|
+
this.pianoSVG_?.setAttribute('transform', 'translate(' + this.animationPos_ + ',0)')
|
|
684
|
+
requestAnimationFrame(this.stepOctaveAnimation_.bind(this))
|
|
782
685
|
}
|
|
783
686
|
|
|
784
687
|
doValueUpdate_(newValue: string) {
|
|
785
|
-
super.doValueUpdate_(newValue)
|
|
688
|
+
super.doValueUpdate_(newValue)
|
|
786
689
|
|
|
787
690
|
if (!this.textElement_) {
|
|
788
691
|
// Not rendered yet.
|
|
789
|
-
return
|
|
692
|
+
return
|
|
790
693
|
}
|
|
791
694
|
|
|
792
|
-
this.updateSelection_()
|
|
695
|
+
this.updateSelection_()
|
|
793
696
|
}
|
|
794
697
|
|
|
795
698
|
/**
|
|
796
699
|
* For a MIDI note number, find the index of the corresponding piano key.
|
|
797
|
-
*
|
|
798
700
|
* @param noteNum The note number.
|
|
799
701
|
* @returns The index of the piano key.
|
|
800
702
|
*/
|
|
801
703
|
private noteNumToKeyIndex_(noteNum: number): number {
|
|
802
|
-
return Math.floor(noteNum) - this.displayedOctave_ * 12
|
|
704
|
+
return Math.floor(noteNum) - (this.displayedOctave_ ?? 0) * 12
|
|
803
705
|
}
|
|
804
706
|
|
|
805
707
|
/**
|
|
806
708
|
* Update the selected note and labels on the field.
|
|
807
709
|
*/
|
|
808
710
|
private updateSelection_() {
|
|
809
|
-
const noteNum = Number(this.getText())
|
|
711
|
+
const noteNum = Number(this.getText())
|
|
810
712
|
|
|
811
713
|
// If the note is outside the currently displayed octave, update it
|
|
812
714
|
if (
|
|
@@ -814,65 +716,65 @@ export class FieldNote extends Blockly.FieldTextInput {
|
|
|
814
716
|
noteNum > this.displayedOctave_ * 12 + 12 ||
|
|
815
717
|
noteNum < this.displayedOctave_ * 12
|
|
816
718
|
) {
|
|
817
|
-
this.displayedOctave_ = Math.floor(noteNum / 12)
|
|
719
|
+
this.displayedOctave_ = Math.floor(noteNum / 12)
|
|
818
720
|
}
|
|
819
721
|
|
|
820
|
-
const index = this.noteNumToKeyIndex_(noteNum)
|
|
722
|
+
const index = this.noteNumToKeyIndex_(noteNum)
|
|
821
723
|
|
|
822
724
|
// Clear the highlight on all keys
|
|
823
|
-
this.keySVGs_.forEach(
|
|
824
|
-
const isBlack = svg.getAttribute(
|
|
825
|
-
if (isBlack ===
|
|
826
|
-
svg.setAttribute(
|
|
725
|
+
this.keySVGs_.forEach(svg => {
|
|
726
|
+
const isBlack = svg.getAttribute('data-isBlack')
|
|
727
|
+
if (isBlack === 'true') {
|
|
728
|
+
svg.setAttribute('fill', FieldNote.BLACK_KEY_COLOR)
|
|
827
729
|
} else {
|
|
828
|
-
svg.setAttribute(
|
|
730
|
+
svg.setAttribute('fill', FieldNote.WHITE_KEY_COLOR)
|
|
829
731
|
}
|
|
830
|
-
})
|
|
732
|
+
})
|
|
831
733
|
// Set the highlight on the selected key
|
|
832
734
|
if (this.keySVGs_[index]) {
|
|
833
|
-
this.keySVGs_[index].setAttribute(
|
|
735
|
+
this.keySVGs_[index].setAttribute('fill', FieldNote.KEY_SELECTED_COLOR)
|
|
834
736
|
// Update the note name text
|
|
835
|
-
const noteName = FieldNote.KEY_INFO[index].name
|
|
836
|
-
this.noteNameText_
|
|
837
|
-
noteName +
|
|
737
|
+
const noteName = FieldNote.KEY_INFO[index].name
|
|
738
|
+
if (this.noteNameText_) {
|
|
739
|
+
this.noteNameText_.textContent = noteName + ' (' + Math.floor(noteNum) + ')'
|
|
740
|
+
}
|
|
838
741
|
// Update the low and high C note names
|
|
839
|
-
const lowCNum = this.displayedOctave_ * 12
|
|
840
|
-
this.lowCText_.textContent =
|
|
841
|
-
this.highCText_.textContent =
|
|
742
|
+
const lowCNum = (this.displayedOctave_ ?? 0) * 12
|
|
743
|
+
if (this.lowCText_) this.lowCText_.textContent = 'C(' + lowCNum + ')'
|
|
744
|
+
if (this.highCText_) this.highCText_.textContent = 'C(' + (lowCNum + 12) + ')'
|
|
842
745
|
}
|
|
843
746
|
}
|
|
844
747
|
|
|
845
748
|
/**
|
|
846
749
|
* Ensure that only a valid MIDI note number may be entered.
|
|
847
|
-
*
|
|
848
750
|
* @param text The user's text.
|
|
849
751
|
* @returns A string representing a valid note number, or null if invalid.
|
|
850
752
|
*/
|
|
851
753
|
doClassValidation_(text: string): string | null {
|
|
852
754
|
if (text === null) {
|
|
853
|
-
return null
|
|
755
|
+
return null
|
|
854
756
|
}
|
|
855
|
-
|
|
757
|
+
let n = parseFloat(text || '0')
|
|
856
758
|
if (isNaN(n)) {
|
|
857
|
-
return null
|
|
759
|
+
return null
|
|
858
760
|
}
|
|
859
761
|
if (n < 0) {
|
|
860
|
-
n = 0
|
|
762
|
+
n = 0
|
|
861
763
|
}
|
|
862
764
|
if (n > FieldNote.MAX_NOTE) {
|
|
863
|
-
n = FieldNote.MAX_NOTE
|
|
765
|
+
n = FieldNote.MAX_NOTE
|
|
864
766
|
}
|
|
865
|
-
return String(n)
|
|
767
|
+
return String(n)
|
|
866
768
|
}
|
|
867
769
|
}
|
|
868
770
|
|
|
869
771
|
interface FieldNoteJsonConfig extends Blockly.FieldTextInputFromJsonConfig {
|
|
870
|
-
note: string
|
|
772
|
+
note: string
|
|
871
773
|
}
|
|
872
774
|
|
|
873
775
|
/**
|
|
874
776
|
* Register the field and any dependencies.
|
|
875
777
|
*/
|
|
876
778
|
export function registerFieldNote() {
|
|
877
|
-
Blockly.fieldRegistry.register(
|
|
779
|
+
Blockly.fieldRegistry.register('field_note', FieldNote)
|
|
878
780
|
}
|