diff --git a/core/serialization/blocks.js b/core/serialization/blocks.js index 4267c295f3f..60a54021a4b 100644 --- a/core/serialization/blocks.js +++ b/core/serialization/blocks.js @@ -20,6 +20,7 @@ const Connection = goog.requireType('Blockly.Connection'); const Events = goog.require('Blockly.Events'); const {MissingBlockType, MissingConnection, BadConnectionCheck} = goog.require('Blockly.serialization.exceptions'); +const Size = goog.require('Blockly.utils.Size'); // eslint-disable-next-line no-unused-vars const Workspace = goog.requireType('Blockly.Workspace'); const Xml = goog.require('Blockly.Xml'); @@ -54,6 +55,7 @@ exports.ConnectionState = ConnectionState; * inline: (boolean|undefined), * data: (string|undefined), * extra-state: *, + * icons: (!Object|undefined), * fields: (!Object|undefined), * inputs: (!Object|undefined), * next: (!ConnectionState|undefined) @@ -99,6 +101,7 @@ const save = function( } saveAttributes(block, state); saveExtraState(block, state); + saveIcons(block, state); saveFields(block, state); if (addInputBlocks) { saveInputBlocks(block, state); @@ -171,6 +174,25 @@ const saveExtraState = function(block, state) { } }; +/** + * Adds the state of all of the icons on the block to the given state object. + * @param {!Block} block The block to serialize the icon state of. + * @param {!State} state The state object to append to. + */ +const saveIcons = function(block, state) { + // TODO(#2105): Remove this logic and put it in the icon. + if (block.getCommentText()) { + state['icons'] = { + 'comment': { + 'text': block.getCommentText(), + 'pinned': block.commentModel.pinned, + 'height': Math.round(block.commentModel.size.height), + 'width': Math.round(block.commentModel.size.width), + } + }; + } +}; + /** * Adds the state of all of the fields on the block to the given state object. * @param {!Block} block The block to serialize the field state of. @@ -178,7 +200,7 @@ const saveExtraState = function(block, state) { */ const saveFields = function(block, state) { let hasFieldState = false; - let fields = Object.create(null); + const fields = Object.create(null); for (let i = 0; i < block.inputList.length; i++) { const input = block.inputList[i]; for (let j = 0; j < input.fieldRow.length; j++) { @@ -322,7 +344,7 @@ const loadInternal = function(state, workspace, parentConnection = undefined) { loadAttributes(block, state); loadExtraState(block, state); tryToConnectParent(parentConnection, block, state); - // loadIcons(block, state); + loadIcons(block, state); loadFields(block, state); loadInputBlocks(block, state); loadNextBlocks(block, state); @@ -427,6 +449,29 @@ const tryToConnectParent = function(parentConnection, child, state) { } }; +/** + * Applies icon state to the icons on the block, based on the given state + * object. + * @param {!Block} block The block to set the icon state of. + * @param {!State} state The state object to reference. + */ +const loadIcons = function(block, state) { + if (!state['icons']) { + return; + } + // TODO(#2105): Remove this logic and put it in the icon. + const comment = state['icons']['comment']; + if (comment) { + block.setCommentText(comment['text']); + block.commentModel.pinned = comment['pinned']; + block.commentModel.size = new Size(comment['width'], comment['height']); + if (comment['pinned'] && block.getCommentIcon && !block.isInFlyout) { + // Give the block a chance to be positioned and rendered before showing. + setTimeout(() => block.getCommentIcon().setVisible(true), 1); + } + } +}; + /** * Applies any field information available on the state object to the block. * @param {!Block} block The block to set the field state of. diff --git a/tests/deps.js b/tests/deps.js index a96fa877c1f..13a8463ada0 100644 --- a/tests/deps.js +++ b/tests/deps.js @@ -196,7 +196,7 @@ goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Ren goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.ContextMenuItems', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.ShortcutItems', 'Blockly.Themes.Classic', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.serialization.workspaces', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer']); goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar'], ['Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/scrollbar_pair.js', ['Blockly.ScrollbarPair'], ['Blockly.Events', 'Blockly.Scrollbar', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); -goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Events', 'Blockly.Xml', 'Blockly.inputTypes', 'Blockly.serialization.exceptions'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Events', 'Blockly.Xml', 'Blockly.inputTypes', 'Blockly.serialization.exceptions', 'Blockly.utils.Size'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/exceptions.js', ['Blockly.serialization.exceptions'], [], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/variables.js', ['Blockly.serialization.variables'], ['Blockly.Events'], {'lang': 'es6', 'module': 'goog'}); goog.addDependency('../../core/serialization/workspaces.js', ['Blockly.serialization.workspaces'], ['Blockly.Events', 'Blockly.Workspace', 'Blockly.serialization.blocks', 'Blockly.serialization.variables', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); diff --git a/tests/mocha/jso_serialization_test.js b/tests/mocha/jso_serialization_test.js index 83647f5e7ee..51dfe3c27d3 100644 --- a/tests/mocha/jso_serialization_test.js +++ b/tests/mocha/jso_serialization_test.js @@ -274,6 +274,67 @@ suite('JSO Serialization', function() { }); }); + suite('Icons', function() { + suite('Comment', function() { + test('Basic', function() { + const block = this.workspace.newBlock('row_block'); + block.setCommentText('test'); + const jso = Blockly.serialization.blocks.save(block); + assertProperty( + jso, + 'icons', + { + 'comment': { + 'text': 'test', + 'pinned': false, + 'height': 80, + 'width': 160, + } + }, + ); + }); + + test('Pinned', function() { + const block = this.workspace.newBlock('row_block'); + block.setCommentText('test'); + block.commentModel.pinned = true; + const jso = Blockly.serialization.blocks.save(block); + assertProperty( + jso, + 'icons', + { + 'comment': { + 'text': 'test', + 'pinned': true, + 'height': 80, + 'width': 160, + } + }, + ); + }); + + test('Size', function() { + const block = this.workspace.newBlock('row_block'); + block.setCommentText('test'); + block.commentModel.size.height = 40; + block.commentModel.size.width = 320; + const jso = Blockly.serialization.blocks.save(block); + assertProperty( + jso, + 'icons', + { + 'comment': { + 'text': 'test', + 'pinned': false, + 'height': 40, + 'width': 320, + } + }, + ); + }); + }); + }); + suite('Fields', function() { class StringStateField extends Blockly.Field { constructor(value, validator = undefined, config = undefined) { diff --git a/tests/mocha/serializer_test.js b/tests/mocha/serializer_test.js index 466c16859c7..4fd20f901f3 100644 --- a/tests/mocha/serializer_test.js +++ b/tests/mocha/serializer_test.js @@ -1834,5 +1834,4 @@ var runSerializerTestSuite = (serializer, deserializer, testSuite) => { }; runSerializerTestSuite(null, null, Serializer); -Serializer.Icons.skip = true; runSerializerTestSuite(state => state, state => state, Serializer);