From b11423a122a83004fb802fd1e736a3b1d226b12f Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 15 Mar 2021 08:51:15 +0100 Subject: [PATCH 01/92] restored scenes branch --- HISTORY.md | 9 ++++ snap.html | 5 ++- src/gui.js | 64 ++++++++++++++++++++-------- src/scenes.js | 73 +++++++++++++++++++++++++++++++ src/store.js | 116 ++++++++++++++------------------------------------ 5 files changed, 164 insertions(+), 103 deletions(-) create mode 100644 src/scenes.js diff --git a/HISTORY.md b/HISTORY.md index d3bfffc8..0f5ef5ae 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,15 @@ ## in development: ### 2021-03-15 +* gui: marked methods for scene refactorings + +### 2021-03-12 +* scenes, gui, store: added scenes class + +### 2021-03-11 +* gui, store: refactor loading a project into the IDE + +### 2021-03-09 * new dev version ## 6.7.1 diff --git a/snap.html b/snap.html index ba14d4c1..6f888f0b 100755 --- a/snap.html +++ b/snap.html @@ -3,7 +3,7 @@ - Snap! 6.7.2 - dev - Build Your Own Blocks + Snap! 7 - dev - Build Your Own Blocks @@ -11,6 +11,7 @@ + @@ -20,7 +21,7 @@ - + diff --git a/src/gui.js b/src/gui.js index b8d4ac82..1e37c084 100644 --- a/src/gui.js +++ b/src/gui.js @@ -74,7 +74,8 @@ CommandBlockMorph, BooleanSlotMorph, RingReporterSlotMorph, ScriptFocusMorph, BlockLabelPlaceHolderMorph, SpeechBubbleMorph, XML_Element, WatcherMorph, WHITE, BlockRemovalDialogMorph,TableMorph, isSnapObject, isRetinaEnabled, SliderMorph, disableRetinaSupport, enableRetinaSupport, isRetinaSupported, MediaRecorder, -Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ +Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK, +Scene*/ // Global stuff //////////////////////////////////////////////////////// @@ -227,7 +228,8 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.source = null; this.serializer = new SnapSerializer(); - this.globalVariables = new VariableFrame(); + this.scene = new Scene(); + this.globalVariables = this.scene.globalVariables; this.currentSprite = new SpriteMorph(this.globalVariables); this.sprites = new List([this.currentSprite]); this.currentCategory = 'motion'; @@ -4353,7 +4355,7 @@ IDE_Morph.prototype.aboutSnap = function () { module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn, world = this.world(); - aboutTxt = 'Snap! 6.7.2 - dev -\nBuild Your Own Blocks\n\n' + aboutTxt = 'Snap! 7 - dev -\nBuild Your Own Blocks\n\n' + 'Copyright \u24B8 2008-2021 Jens M\u00F6nig and ' + 'Brian Harvey\n' + 'jens@moenig.org, bh@cs.berkeley.edu\n\n' @@ -4586,7 +4588,7 @@ IDE_Morph.prototype.editProjectNotes = function () { text.edit(); }; -IDE_Morph.prototype.newProject = function () { +IDE_Morph.prototype.newProject = function () { // +++ this.source = this.cloud.username ? 'cloud' : null; if (this.stage) { this.stage.destroy(); @@ -4594,6 +4596,9 @@ IDE_Morph.prototype.newProject = function () { if (location.hash.substr(0, 6) !== '#lang:') { location.hash = ''; } + // +++ this.switchToScene(new Scene()); + + this.globalVariables = new VariableFrame(); this.currentSprite = new SpriteMorph(this.globalVariables); this.sprites = new List([this.currentSprite]); @@ -5087,7 +5092,7 @@ IDE_Morph.prototype.openProjectString = function (str, callback) { ]); }; -IDE_Morph.prototype.rawOpenProjectString = function (str) { +IDE_Morph.prototype.rawOpenProjectString = function (str) { // +++ this.toggleAppMode(false); this.spriteBar.tabBar.tabTo('scripts'); StageMorph.prototype.hiddenPrimitives = {}; @@ -5102,17 +5107,15 @@ IDE_Morph.prototype.rawOpenProjectString = function (str) { this.hasUnsavedEdits = false; if (Process.prototype.isCatchingErrors) { try { - this.serializer.openProject( - this.serializer.load(str, this), - this + this.switchToScene( + this.serializer.load(str, this) ); } catch (err) { this.showMessage('Load failed: ' + err); } } else { - this.serializer.openProject( - this.serializer.load(str, this), - this + this.switchToScene( + this.serializer.load(str, this) ); } this.stopFastTracking(); @@ -5130,7 +5133,7 @@ IDE_Morph.prototype.openCloudDataString = function (str) { ]); }; -IDE_Morph.prototype.rawOpenCloudDataString = function (str) { +IDE_Morph.prototype.rawOpenCloudDataString = function (str) { // +++ var model; StageMorph.prototype.hiddenPrimitives = {}; StageMorph.prototype.codeMappings = {}; @@ -5146,13 +5149,12 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) { try { model = this.serializer.parse(str); this.serializer.loadMediaModel(model.childNamed('media')); - this.serializer.openProject( + this.switchToScene( this.serializer.loadProjectModel( model.childNamed('project'), this, model.attributes.remixID - ), - this + ) ); } catch (err) { this.showMessage('Load failed: ' + err); @@ -5160,13 +5162,12 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) { } else { model = this.serializer.parse(str); this.serializer.loadMediaModel(model.childNamed('media')); - this.serializer.openProject( + this.switchToScene( this.serializer.loadProjectModel( model.childNamed('project'), this, model.attributes.remixID - ), - this + ) ); } this.stopFastTracking(); @@ -5359,6 +5360,33 @@ IDE_Morph.prototype.openProject = function (name) { } }; +IDE_Morph.prototype.switchToScene = function (scene) { // +++ + var sprites = []; + if (!scene || !scene.stage) { + return; + } + this.siblings().forEach(morph => + morph.destroy() + ); + this.scene = scene; + this.projectName = scene.name; + this.projectNotes = scene.notes || ''; + this.globalVariables = scene.globalVariables; + this.stage.destroy(); + this.add(scene.stage); + this.stage = scene.stage; + sprites = this.stage.children.filter( + child => child instanceof SpriteMorph + ); + sprites.sort((x, y) => x.idx - y.idx); + this.sprites = new List(sprites); + this.stage.pauseGenericHatBlocks(); + this.createCorral(); + this.selectSprite(sprites[0] || this.stage); + this.fixLayout(); + this.world().keyboardFocus = this.stage; +}; + IDE_Morph.prototype.setURL = function (str) { // Set the URL to a project's XML contents location.hash = this.projectsInURLs ? str : ''; diff --git a/src/scenes.js b/src/scenes.js new file mode 100644 index 00000000..1174385f --- /dev/null +++ b/src/scenes.js @@ -0,0 +1,73 @@ +/* + + scenes.js + + multi-scene support for Snap! + + written by Jens Mönig + jens@moenig.org + + Copyright (C) 2021 by Jens Mönig + + This file is part of Snap!. + + Snap! is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + + prerequisites: + -------------- + needs morphic.js and objects.js + + toc + --- + the following list shows the order in which all constructors are + defined. Use this list to locate code in this document: + + Scene + + credits + ------- + scenes have been inspired by Ted Kaehlers's personal demos of HyperCard + and many discussions with Ted about the design and practice of HyperCard, + and by personal discussions with Wolfgang Slany about his design of + scenes in Catrobat/PocketCode, which I love and admire. + +*/ + +// Global stuff //////////////////////////////////////////////////////// + +/*global modules, VariableFrame, aStageMorph*/ + +modules.scenes = '2021-March-12'; + +// Scene ///////////////////////////////////////////////////////// + +// I am a container for a Snap! stage, scene-global variables +// and its associated settings. +// I can be used as a slide in a presentation, a chapter in a narrative, +// a level in a game, etc. + +// Scene instance creation: + +function Scene(aStageMorph) { + this.name = null; + this.notes = null; + this.globalVariables = aStageMorph ? + aStageMorph.globalVariables() : new VariableFrame(); + this.stage = aStageMorph || new StageMorph(this.globalVariables); + + // for deserializing - do not persist + this.sprites = {}; + this.targetStage = null; +} diff --git a/src/store.js b/src/store.js index 46274bd2..5a272eb4 100644 --- a/src/store.js +++ b/src/store.js @@ -27,7 +27,7 @@ prerequisites: -------------- - needs morphic.js, xml.js, and most of Snap!'s other modules + needs morphic.js, xml.js, scenes.js and most of Snap!'s other modules hierarchy @@ -49,19 +49,18 @@ */ -/*global modules, XML_Element, VariableFrame, StageMorph, SpriteMorph, -WatcherMorph, Point, CustomBlockDefinition, Context, ReporterBlockMorph, +/*global modules, XML_Element, VariableFrame, StageMorph, SpriteMorph, console, +WatcherMorph, Point, CustomBlockDefinition, Context, ReporterBlockMorph, Sound, CommandBlockMorph, detect, CustomCommandBlockMorph, CustomReporterBlockMorph, -Color, List, newCanvas, Costume, Sound, Audio, IDE_Morph, ScriptsMorph, +Color, List, newCanvas, Costume, Audio, IDE_Morph, ScriptsMorph, ArgLabelMorph, BlockMorph, ArgMorph, InputSlotMorph, TemplateSlotMorph, CommandSlotMorph, FunctionSlotMorph, MultiArgMorph, ColorSlotMorph, nop, CommentMorph, isNil, -localize, sizeOf, ArgLabelMorph, SVG_Costume, MorphicPreferences, Process, -SyntaxElementMorph, Variable, isSnapObject, console, BooleanSlotMorph, -normalizeCanvas, contains*/ +localize, SVG_Costume, MorphicPreferences, Process, isSnapObject, Variable, +SyntaxElementMorph, BooleanSlotMorph, normalizeCanvas, contains, Scene*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-March-09'; +modules.store = '2021-March-12'; // XML_Serializer /////////////////////////////////////////////////////// @@ -279,7 +278,7 @@ function SnapSerializer() { // SnapSerializer initialization: SnapSerializer.prototype.init = function () { - this.project = {}; + this.scene = new Scene(); this.objects = {}; this.mediaDict = {}; }; @@ -331,11 +330,11 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide, remixID) { SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { // private - var project = {sprites: {}}, + var project = new Scene(), model, nameID; - this.project = project; + this.scene = project; model = {project: xmlNode }; if (+xmlNode.attributes.version > this.version) { @@ -363,14 +362,13 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { project.notes = model.notes.contents; } model.globalVariables = model.project.childNamed('variables'); - project.globalVariables = new VariableFrame(); /* Stage */ model.stage = model.project.require('stage'); StageMorph.prototype.frameRate = 0; - project.stage = new StageMorph(project.globalVariables); project.stage.remixID = remixID; + if (Object.prototype.hasOwnProperty.call( model.stage.attributes, 'id' @@ -484,10 +482,10 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { ); // restore inheritance and nesting associations - this.project.stage.children.forEach(sprite => { + this.scene.stage.children.forEach(sprite => { var exemplar, anchor; if (sprite.inheritanceInfo) { // only sprites can inherit - exemplar = this.project.sprites[ + exemplar = this.scene.sprites[ sprite.inheritanceInfo.exemplar ]; if (exemplar) { @@ -497,14 +495,14 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { sprite.updatePropagationCache(); } if (sprite.nestingInfo) { // only sprites may have nesting info - anchor = this.project.sprites[sprite.nestingInfo.anchor]; + anchor = this.scene.sprites[sprite.nestingInfo.anchor]; if (anchor) { anchor.attachPart(sprite); } sprite.rotatesWithAnchor = (sprite.nestingInfo.synch === 'true'); } }); - this.project.stage.children.forEach(sprite => { + this.scene.stage.children.forEach(sprite => { var costume; if (sprite.nestingInfo) { // only sprites may have nesting info sprite.nestingScale = +(sprite.nestingInfo.scale || sprite.scale); @@ -621,7 +619,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { }); // clear sprites' inherited methods caches, if any - this.project.stage.children.forEach( + this.scene.stage.children.forEach( sprite => sprite.inheritedMethodsCache = [] ); @@ -632,14 +630,11 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { SnapSerializer.prototype.loadBlocks = function (xmlString, targetStage) { // public - answer a new Array of custom block definitions // represented by the given XML String - var stage = new StageMorph(), - model; + var stage, model; - this.project = { - stage: stage, - sprites: {}, - targetStage: targetStage // for secondary custom block def look-up - }; + this.scene = new Scene(); + this.scene.targetStage = targetStage; // for secondary block def look-up + stage = this.scene.stage; model = this.parse(xmlString); if (+model.attributes.version > this.version) { throw 'Module uses newer version of Serializer'; @@ -653,7 +648,7 @@ SnapSerializer.prototype.loadBlocks = function (xmlString, targetStage) { this.objects = {}; stage.globalBlocks.forEach(def => def.receiver = null); this.objects = {}; - this.project = {}; + this.scene = new Scene(); this.mediaDict = {}; return stage.globalBlocks; }; @@ -663,11 +658,8 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { // into the current project of the ide var model, project; - project = this.project = { - globalVariables: ide.globalVariables, - stage: ide.stage, - sprites: {} - }; + this.scene = new Scene(ide.stage); + project = this.scene; project.sprites[project.stage.name] = project.stage; model = this.parse(xmlString); @@ -740,7 +732,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { }); this.objects = {}; - this.project = {}; + this.scene = new Scene(); this.mediaDict = {}; ide.stage.fixLayout(); @@ -1112,9 +1104,9 @@ SnapSerializer.prototype.loadScript = function (model, object) { // Check whether we're importing a single script, not a script as part of a // whole project - if (!this.project.stage) { - this.project.stage = object.parentThatIsA(StageMorph); - this.project.targetStage = this.project.stage; + if (!this.scene.stage) { + this.scene.stage = object.parentThatIsA(StageMorph); + this.scene.targetStage = this.scene.stage; } model.children.forEach(child => { @@ -1185,15 +1177,15 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter, object) { } } else if (model.tag === 'custom-block') { isGlobal = model.attributes.scope ? false : true; - receiver = isGlobal ? this.project.stage : object; + receiver = isGlobal ? this.scene.stage : object; if (isGlobal) { info = detect( receiver.globalBlocks, block => block.blockSpec() === model.attributes.s ); - if (!info && this.project.targetStage) { // importing block files + if (!info && this.scene.targetStage) { // importing block files info = detect( - this.project.targetStage.globalBlocks, + this.scene.targetStage.globalBlocks, block => block.blockSpec() === model.attributes.s ); } @@ -1411,13 +1403,13 @@ SnapSerializer.prototype.loadValue = function (model, object) { }); return v; case 'sprite': - v = new SpriteMorph(this.project.globalVariables); + v = new SpriteMorph(this.scene.globalVariables); if (model.attributes.id) { this.objects[model.attributes.id] = v; } if (model.attributes.name) { v.name = model.attributes.name; - this.project.sprites[model.attributes.name] = v; + this.scene.sprites[model.attributes.name] = v; } if (model.attributes.idx) { v.idx = +model.attributes.idx; @@ -1435,7 +1427,7 @@ SnapSerializer.prototype.loadValue = function (model, object) { if (model.attributes.pan) { v.pan = +model.attributes.pan; } - this.project.stage.add(v); + this.scene.stage.add(v); v.scale = parseFloat(model.attributes.scale || '1'); v.rotationStyle = parseFloat( model.attributes.rotation || '1' @@ -1617,48 +1609,6 @@ SnapSerializer.prototype.loadColor = function (colorString) { ); }; -SnapSerializer.prototype.openProject = function (project, ide) { - var stage = ide.stage, - sprites = [], - sprite; - if (!project || !project.stage) { - return; - } - ide.siblings().forEach(morph => - morph.destroy() - ); - ide.projectName = project.name; - ide.projectNotes = project.notes || ''; - if (ide.globalVariables) { - ide.globalVariables = project.globalVariables; - } - if (stage) { - stage.destroy(); - } - ide.add(project.stage); - ide.stage = project.stage; - sprites = ide.stage.children.filter( - child => child instanceof SpriteMorph - ); - sprites.sort((x, y) => x.idx - y.idx); - - ide.sprites = new List(sprites); - sprite = sprites[0] || project.stage; - - if (sizeOf(this.mediaDict) > 0) { - ide.hasChangedMedia = false; - this.mediaDict = {}; - } else { - ide.hasChangedMedia = true; - } - project.stage.fixLayout(); - project.stage.pauseGenericHatBlocks(); - ide.createCorral(); - ide.selectSprite(sprite); - ide.fixLayout(); - ide.world().keyboardFocus = project.stage; -}; - // SnapSerializer XML-representation of objects: // Generics From f4ea4bf25e7b3849a9265db0767e7f62aae4d404 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 17 Mar 2021 18:28:17 +0100 Subject: [PATCH 02/92] de-globalized stage dimensions --- HISTORY.md | 3 +++ snap.html | 10 +++++----- src/gui.js | 46 ++++++++++++++++++++++++---------------------- src/objects.js | 23 +++++++++++++---------- src/paint.js | 25 +++++++++++++++---------- src/sketch.js | 25 +++++++++++++++++-------- src/store.js | 13 ++++++------- 7 files changed, 83 insertions(+), 62 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2b2a4a52..d676143e 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,9 @@ ## in development: +### 2021-03-17 +* objects, gui, paint, sketch, store: de-globalized stage dimensions + ## 6.7.2 * **Notable Changes:** * disabled empty-slot implicit parameter in FOREACH diff --git a/snap.html b/snap.html index 3cccfba1..cfc70098 100755 --- a/snap.html +++ b/snap.html @@ -10,18 +10,18 @@ - + - - + + - + - + diff --git a/src/gui.js b/src/gui.js index 1e37c084..dec19d82 100644 --- a/src/gui.js +++ b/src/gui.js @@ -79,7 +79,7 @@ Scene*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-March-15'; +modules.gui = '2021-March-17'; // Declarations @@ -1092,7 +1092,7 @@ IDE_Morph.prototype.createControlBar = function () { x = Math.min( startButton.left() - (3 * padding + 2 * stageSizeButton.width()), - myself.right() - StageMorph.prototype.dimensions.x * + myself.right() - myself.stage.dimensions.x * (myself.isSmallStage ? myself.stageRatio : 1) ); [stageSizeButton, appModeButton].forEach(button => { @@ -2108,16 +2108,16 @@ IDE_Morph.prototype.setExtent = function (point) { if (this.isEmbedMode) { minExt = new Point(100, 100); } else { - minExt = StageMorph.prototype.dimensions.add( + minExt = this.stage.dimensions.add( this.controlBar.height() + 10 ); } } else { if (this.stageRatio > 1) { - minExt = padding.add(StageMorph.prototype.dimensions); + minExt = padding.add(this.stage.dimensions); } else { minExt = padding.add( - StageMorph.prototype.dimensions.multiplyBy(this.stageRatio) + this.stage.dimensions.multiplyBy(this.stageRatio) ); } } @@ -2245,11 +2245,10 @@ IDE_Morph.prototype.droppedSVG = function (anImage, name) { } // checking if the costume is bigger than the stage and, if so, fit it - if (StageMorph.prototype.dimensions.x < w || - StageMorph.prototype.dimensions.y < h) { + if (this.stage.dimensions.x < w || this.stage.dimensions.y < h) { scale = Math.min( - (StageMorph.prototype.dimensions.x / w), - (StageMorph.prototype.dimensions.y / h) + (this.stage.dimensions.x / w), + (this.stage.dimensions.y / h) ); normalizing = true; w = w * scale; @@ -4602,7 +4601,7 @@ IDE_Morph.prototype.newProject = function () { // +++ this.globalVariables = new VariableFrame(); this.currentSprite = new SpriteMorph(this.globalVariables); this.sprites = new List([this.currentSprite]); - StageMorph.prototype.dimensions = new Point(480, 360); + // +++ StageMorph.prototype.dimensions = new Point(480, 360); StageMorph.prototype.hiddenPrimitives = {}; StageMorph.prototype.codeMappings = {}; StageMorph.prototype.codeHeaders = {}; @@ -6168,7 +6167,7 @@ IDE_Morph.prototype.userSetStageSize = function () { this ).promptVector( "Stage size", - StageMorph.prototype.dimensions, + this.stage.dimensions, new Point(480, 360), 'Stage width', 'Stage height', @@ -6186,16 +6185,16 @@ IDE_Morph.prototype.setStageExtent = function (aPoint) { function zoom() { myself.step = function () { var delta = ext.subtract( - StageMorph.prototype.dimensions + myself.stage.dimensions ).divideBy(2); if (delta.abs().lt(new Point(5, 5))) { - StageMorph.prototype.dimensions = ext; + myself.stage.dimensions = ext; delete myself.step; } else { - StageMorph.prototype.dimensions = - StageMorph.prototype.dimensions.add(delta); + myself.stage.dimensions = + myself.stage.dimensions.add(delta); } - myself.stage.setExtent(StageMorph.prototype.dimensions); + myself.stage.setExtent(myself.stage.dimensions); myself.stage.clearPenTrails(); myself.fixLayout(); this.setExtent(world.extent()); @@ -6210,8 +6209,8 @@ IDE_Morph.prototype.setStageExtent = function (aPoint) { if (this.isAnimating) { zoom(); } else { - StageMorph.prototype.dimensions = ext; - this.stage.setExtent(StageMorph.prototype.dimensions); + this.stage.dimensions = ext; + this.stage.setExtent(this.stage.dimensions); this.stage.clearPenTrails(); this.fixLayout(); this.setExtent(world.extent()); @@ -9597,11 +9596,14 @@ WardrobeMorph.prototype.removeCostumeAt = function (idx) { }; WardrobeMorph.prototype.paintNew = function () { - var cos = new Costume( + var ide = this.parentThatIsA(IDE_Morph), + cos = new Costume( newCanvas(null, true), - this.sprite.newCostumeName(localize('Untitled')) - ), - ide = this.parentThatIsA(IDE_Morph); + this.sprite.newCostumeName(localize('Untitled')), + null, // rotation center + null, // don't shrink-to-fit + ide.stage.dimensions // max extent + ); cos.edit( this.world(), diff --git a/src/objects.js b/src/objects.js index 6c74f260..c1ef99b9 100644 --- a/src/objects.js +++ b/src/objects.js @@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph, localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph, AlignmentMorph, Process, WorldMap, copyCanvas, useBlurredShadows*/ -modules.objects = '2021-March-09'; +modules.objects = '2021-March-17'; var SpriteMorph; var StageMorph; @@ -3732,7 +3732,9 @@ SpriteMorph.prototype.doWearPreviousCostume = function () { }; SpriteMorph.prototype.doSwitchToCostume = function (id, noShadow) { - var w = 0, h = 0; + var w = 0, + h = 0, + stage; if (id instanceof List) { // try to turn a list of pixels into a costume if (this.costume) { // recycle dimensions of current costume @@ -3741,8 +3743,9 @@ SpriteMorph.prototype.doSwitchToCostume = function (id, noShadow) { } if (w * h !== id.length()) { // assume stage's dimensions - w = StageMorph.prototype.dimensions.x; - h = StageMorph.prototype.dimensions.y; + stage = this.parentThatIsA(StageMorph); + w = stage.dimensions.x; + h = stage.dimensions.y; } id = Process.prototype.reportNewCostume( id, @@ -7660,7 +7663,7 @@ StageMorph.uber = FrameMorph.prototype; // StageMorph preferences settings -StageMorph.prototype.dimensions = new Point(480, 360); // unscaled extent +StageMorph.prototype.dimensions = new Point(480, 360); // fallback unscaled ext StageMorph.prototype.frameRate = 0; // unscheduled per default StageMorph.prototype.isCachingPrimitives @@ -7688,6 +7691,7 @@ function StageMorph(globals) { StageMorph.prototype.init = function (globals) { this.name = localize('Stage'); + this.dimensions = new Point(480, 360); // unscaled extent this.instrument = null; this.threads = new ThreadManager(); this.variables = new VariableFrame(globals || null, this); @@ -9886,10 +9890,10 @@ SpriteBubbleMorph.prototype.fixLayout = function () { // Costume instance creation -function Costume(canvas, name, rotationCenter, noFit) { +function Costume(canvas, name, rotationCenter, noFit, maxExtent) { this.contents = canvas ? normalizeCanvas(canvas, true) : newCanvas(null, true); - if (!noFit) {this.shrinkToFit(this.maxExtent()); } + if (!noFit) {this.shrinkToFit(maxExtent || this.maxExtent()); } this.name = name || null; this.rotationCenter = rotationCenter || this.center(); this.version = Date.now(); // for observer optimization @@ -10094,7 +10098,7 @@ Costume.prototype.edit = function (aWorld, anIDE, isnew, oncancel, onsubmit) { editor.openIn( aWorld, isnew ? - newCanvas(StageMorph.prototype.dimensions, true) : + newCanvas(anIDE.stage.dimensions, true) : this.contents, isnew ? null : @@ -10341,7 +10345,7 @@ SVG_Costume.prototype.edit = function ( editor.oncancel = oncancel || nop; editor.openIn( aWorld, - isnew ? newCanvas(StageMorph.prototype.dimensions) : this.contents, + isnew ? newCanvas(anIDE.stage.dimensions) : this.contents, isnew ? new Point(240, 180) : this.rotationCenter, (img, rc, shapes) => { myself.contents = img; @@ -12165,7 +12169,6 @@ StagePrompterMorph.prototype.init = function (question) { if (this.label) {this.add(this.label); } this.add(this.inputField); this.add(this.button); - this.setWidth(StageMorph.prototype.dimensions.x - 20); this.fixLayout(); }; diff --git a/src/paint.js b/src/paint.js index 79b1d146..6645d2d3 100644 --- a/src/paint.js +++ b/src/paint.js @@ -71,18 +71,19 @@ 2020 Apr 14 - Morphic2 migration (Jens) 2020 May 17 - Pipette alpha fix (Joan) - 2020 July 13 - modified scale buttons (Jadga) + 2020 Jul 13 - modified scale buttons (Jadga) + + 2021 Mar 17 - moved stage dimension handling to scenes (Jens) */ -/*global Point, Rectangle, DialogBoxMorph, AlignmentMorph, PushButtonMorph, -Color, SymbolMorph, newCanvas, Morph, StringMorph, Costume, SpriteMorph, nop, -localize, InputFieldMorph, SliderMorph, ToggleMorph, ToggleButtonMorph, -BoxMorph, modules, radians, MorphicPreferences, getDocumentPositionOf, -StageMorph, isNil, SVG_Costume*/ +/*global Point, Rectangle, DialogBoxMorph, AlignmentMorph, PushButtonMorph, nop, +Color, SymbolMorph, newCanvas, Morph, StringMorph, Costume, SpriteMorph, isNil, +localize, InputFieldMorph, SliderMorph, ToggleMorph, ToggleButtonMorph, modules, +BoxMorph, radians, MorphicPreferences, getDocumentPositionOf, SVG_Costume*/ // Global stuff //////////////////////////////////////////////////////// -modules.paint = '2020-July-13'; +modules.paint = '2021-March-17'; // Declarations @@ -106,6 +107,7 @@ function PaintEditorMorph() { PaintEditorMorph.prototype.init = function () { // additional properties: + this.ide = null; this.paper = null; // paint canvas this.oncancel = null; @@ -116,15 +118,16 @@ PaintEditorMorph.prototype.init = function () { this.labelString = "Paint Editor"; this.createLabel(); - // build contents: - this.buildContents(); + // building the contents happens when I am opened with an IDE + // so my extent can be adjusted accordingly (jens) + // this.buildContents(); }; PaintEditorMorph.prototype.buildContents = function () { var myself = this; this.paper = new PaintCanvasMorph(function () {return myself.shift; }); - this.paper.setExtent(StageMorph.prototype.dimensions); + this.paper.setExtent(this.ide.stage.dimensions); this.addBody(new AlignmentMorph('row', this.padding)); this.controls = new AlignmentMorph('column', this.padding / 2); @@ -293,6 +296,8 @@ PaintEditorMorph.prototype.openIn = function ( this.callback = callback || nop; this.ide = anIDE; + this.buildContents(); + this.processKeyUp = function () { myself.shift = false; myself.propertiesControls.constrain.refresh(); diff --git a/src/sketch.js b/src/sketch.js index aa7eb760..6ffa04e4 100644 --- a/src/sketch.js +++ b/src/sketch.js @@ -54,14 +54,16 @@ - select primary color with right-click (in addition to shift-click) 2020, April 15 (Jens): - migrated to new Morphic2 architecture + 2021, March 17 (Jens): + - moved stage dimension handling to scenes */ -/*global Point, Object, Rectangle, AlignmentMorph, Morph, XML_Element, nop, -PaintColorPickerMorph, Color, SliderMorph, InputFieldMorph, ToggleMorph, -TextMorph, Image, newCanvas, PaintEditorMorph, StageMorph, Costume, isNil, -localize, PaintCanvasMorph, StringMorph, detect, modules*/ +/*global Point, Object, Rectangle, AlignmentMorph, Morph, XML_Element, localize, +PaintColorPickerMorph, Color, SliderMorph, InputFieldMorph, ToggleMorph, isNil, +TextMorph, Image, newCanvas, PaintEditorMorph, Costume, nop, PaintCanvasMorph, +StringMorph, detect, modules*/ -modules.sketch = '2020-July-13'; +modules.sketch = '2021-March-17'; // Declarations @@ -999,7 +1001,7 @@ VectorPaintEditorMorph.prototype.buildEdits = function () { }; VectorPaintEditorMorph.prototype.convertToBitmap = function () { - var canvas = newCanvas(StageMorph.prototype.dimensions), + var canvas = newCanvas(this.ide.stage.dimensions), myself = this; this.object = new Costume(); @@ -1053,7 +1055,14 @@ VectorPaintEditorMorph.prototype.openIn = function ( var myself = this, isEmpty = isNil(shapes) || shapes.length === 0; - VectorPaintEditorMorph.uber.openIn.call(this, world, null, oldrc, callback, anIDE); + VectorPaintEditorMorph.uber.openIn.call( + this, + world, + null, + oldrc, + callback, + anIDE + ); this.ide = anIDE; this.paper.drawNew(); this.paper.changed(); @@ -1203,7 +1212,7 @@ VectorPaintEditorMorph.prototype.buildContents = function() { this.paper.destroy(); this.paper = new VectorPaintCanvasMorph(myself.shift); - this.paper.setExtent(StageMorph.prototype.dimensions); + this.paper.setExtent(this.ide.stage.dimensions); this.body.add(this.paper); this.refreshToolButtons(); diff --git a/src/store.js b/src/store.js index 5a272eb4..a8b806cd 100644 --- a/src/store.js +++ b/src/store.js @@ -60,7 +60,7 @@ SyntaxElementMorph, BooleanSlotMorph, normalizeCanvas, contains, Scene*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-March-12'; +modules.store = '2021-March-17'; // XML_Serializer /////////////////////////////////////////////////////// @@ -411,16 +411,15 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { project.pentrails.src = model.pentrails.contents; } project.stage.setTempo(model.stage.attributes.tempo); - StageMorph.prototype.dimensions = new Point(480, 360); if (model.stage.attributes.width) { - StageMorph.prototype.dimensions.x = + project.stage.dimensions.x = Math.max(+model.stage.attributes.width, 240); } if (model.stage.attributes.height) { - StageMorph.prototype.dimensions.y = + project.stage.dimensions.y = Math.max(+model.stage.attributes.height, 180); } - project.stage.setExtent(StageMorph.prototype.dimensions); + project.stage.setExtent(project.stage.dimensions); SpriteMorph.prototype.useFlatLineEnds = model.stage.attributes.lines === 'flat'; BooleanSlotMorph.prototype.isTernary = @@ -1692,8 +1691,8 @@ StageMorph.prototype.toXML = function (serializer) { (ide && ide.projectNotes) ? ide.projectNotes : '', thumbdata, this.name, - StageMorph.prototype.dimensions.x, - StageMorph.prototype.dimensions.y, + this.dimensions.x, + this.dimensions.y, costumeIdx, this.color.r, this.color.g, From 87b8b2471311675995e4a0d6fa55adb4a6de554d Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 18 Mar 2021 12:18:23 +0100 Subject: [PATCH 03/92] more scene-refactorings --- HISTORY.md | 3 ++ snap.html | 6 +-- src/gui.js | 110 +++++++++++++++++-------------------------------- src/objects.js | 3 +- src/scenes.js | 19 +++++++-- 5 files changed, 60 insertions(+), 81 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 04860ea8..5e84b98c 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,9 @@ * repeat stops when encountering a non-numerical counter input, thanks, Stefan! * updated list-utilities library, thanks, Brian! +### 2021-03-18 +* gui, scenes, objects: more scene-refactorings + ### 2021-03-17 * objects, gui, paint, sketch, store: de-globalized stage dimensions * new dev version diff --git a/snap.html b/snap.html index f034ddb6..acfe9f0b 100755 --- a/snap.html +++ b/snap.html @@ -10,9 +10,9 @@ - - - + + + diff --git a/src/gui.js b/src/gui.js index dec19d82..f9b8cc9b 100644 --- a/src/gui.js +++ b/src/gui.js @@ -62,7 +62,7 @@ */ /*global modules, Morph, SpriteMorph, SyntaxElementMorph, Color, Cloud, Audio, -ListWatcherMorph, TextMorph, newCanvas, useBlurredShadows, VariableFrame, Sound, +ListWatcherMorph, TextMorph, newCanvas, useBlurredShadows, Sound, Scene, StringMorph, Point, MenuMorph, morphicVersion, DialogBoxMorph, normalizeCanvas, ToggleButtonMorph, contains, ScrollFrameMorph, StageMorph, PushButtonMorph, sb, InputFieldMorph, FrameMorph, Process, nop, SnapSerializer, ListMorph, detect, @@ -74,12 +74,11 @@ CommandBlockMorph, BooleanSlotMorph, RingReporterSlotMorph, ScriptFocusMorph, BlockLabelPlaceHolderMorph, SpeechBubbleMorph, XML_Element, WatcherMorph, WHITE, BlockRemovalDialogMorph,TableMorph, isSnapObject, isRetinaEnabled, SliderMorph, disableRetinaSupport, enableRetinaSupport, isRetinaSupported, MediaRecorder, -Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK, -Scene*/ +Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-March-17'; +modules.gui = '2021-March-18'; // Declarations @@ -213,7 +212,7 @@ function IDE_Morph(isAutoFill) { this.init(isAutoFill); } -IDE_Morph.prototype.init = function (isAutoFill) { +IDE_Morph.prototype.init = function (isAutoFill) { // +++ // global font setting MorphicPreferences.globalFontFamily = 'Helvetica, Arial'; @@ -230,12 +229,12 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.scene = new Scene(); this.globalVariables = this.scene.globalVariables; - this.currentSprite = new SpriteMorph(this.globalVariables); + this.currentSprite = this.scene.addDefaultSprite(); this.sprites = new List([this.currentSprite]); + this.projectName = this.scene.name; + this.projectNotes = this.scene.notes; this.currentCategory = 'motion'; this.currentTab = 'scripts'; - this.projectName = ''; - this.projectNotes = ''; this.trash = []; // deleted sprites @@ -1377,20 +1376,11 @@ IDE_Morph.prototype.createPaletteHandle = function () { }; IDE_Morph.prototype.createStage = function () { - // assumes that the logo pane has already been created - if (this.stage) {this.stage.destroy(); } - StageMorph.prototype.frameRate = 0; - this.stage = new StageMorph(this.globalVariables); - this.stage.setExtent(this.stage.dimensions); // dimensions are fixed - if (this.currentSprite instanceof SpriteMorph) { - this.currentSprite.setPosition( - this.stage.center().subtract( - this.currentSprite.extent().divideBy(2) - ) - ); - this.stage.add(this.currentSprite); + if (this.stage) { + this.stage.destroy(); } - this.add(this.stage); + this.add(this.scene.stage); + this.stage = this.scene.stage; }; IDE_Morph.prototype.createStageHandle = function () { @@ -2820,7 +2810,7 @@ IDE_Morph.prototype.restore = function () { // IDE_Morph sprite list access -IDE_Morph.prototype.addNewSprite = function () { +IDE_Morph.prototype.addNewSprite = function () { // +++ sceneify var sprite = new SpriteMorph(this.globalVariables), rnd = Process.prototype.reportBasicRandom; @@ -2847,7 +2837,7 @@ IDE_Morph.prototype.addNewSprite = function () { this.selectSprite(sprite); }; -IDE_Morph.prototype.paintNewSprite = function () { +IDE_Morph.prototype.paintNewSprite = function () { // +++ sceneify var sprite = new SpriteMorph(this.globalVariables), cos = new Costume(); @@ -2870,7 +2860,7 @@ IDE_Morph.prototype.paintNewSprite = function () { ); }; -IDE_Morph.prototype.newCamSprite = function () { +IDE_Morph.prototype.newCamSprite = function () { // +++ sceneify var sprite = new SpriteMorph(this.globalVariables), camDialog; @@ -4588,6 +4578,9 @@ IDE_Morph.prototype.editProjectNotes = function () { }; IDE_Morph.prototype.newProject = function () { // +++ + var scene = new Scene(); + + scene.addDefaultSprite(); this.source = this.cloud.username ? 'cloud' : null; if (this.stage) { this.stage.destroy(); @@ -4595,32 +4588,7 @@ IDE_Morph.prototype.newProject = function () { // +++ if (location.hash.substr(0, 6) !== '#lang:') { location.hash = ''; } - // +++ this.switchToScene(new Scene()); - - - this.globalVariables = new VariableFrame(); - this.currentSprite = new SpriteMorph(this.globalVariables); - this.sprites = new List([this.currentSprite]); - // +++ StageMorph.prototype.dimensions = new Point(480, 360); - StageMorph.prototype.hiddenPrimitives = {}; - StageMorph.prototype.codeMappings = {}; - StageMorph.prototype.codeHeaders = {}; - StageMorph.prototype.enableCodeMapping = false; - StageMorph.prototype.enableInheritance = true; - StageMorph.prototype.enableSublistIDs = false; - StageMorph.prototype.enablePenLogging = false; - SpriteMorph.prototype.useFlatLineEnds = false; - Process.prototype.enableLiveCoding = false; - Process.prototype.enableHyperOps = true; - this.hasUnsavedEdits = false; - this.setProjectName(''); - this.projectNotes = ''; - this.trash = []; - this.createStage(); - this.add(this.stage); - this.createCorral(); - this.selectSprite(this.stage.children[0]); - this.fixLayout(); + this.switchToScene(scene); }; IDE_Morph.prototype.save = function () { @@ -5091,19 +5059,9 @@ IDE_Morph.prototype.openProjectString = function (str, callback) { ]); }; -IDE_Morph.prototype.rawOpenProjectString = function (str) { // +++ +IDE_Morph.prototype.rawOpenProjectString = function (str) { this.toggleAppMode(false); this.spriteBar.tabBar.tabTo('scripts'); - StageMorph.prototype.hiddenPrimitives = {}; - StageMorph.prototype.codeMappings = {}; - StageMorph.prototype.codeHeaders = {}; - StageMorph.prototype.enableCodeMapping = false; - StageMorph.prototype.enableInheritance = true; - StageMorph.prototype.enableSublistIDs = false; - StageMorph.prototype.enablePenLogging = false; - Process.prototype.enableLiveCoding = false; - this.trash = []; - this.hasUnsavedEdits = false; if (Process.prototype.isCatchingErrors) { try { this.switchToScene( @@ -5132,18 +5090,9 @@ IDE_Morph.prototype.openCloudDataString = function (str) { ]); }; -IDE_Morph.prototype.rawOpenCloudDataString = function (str) { // +++ +IDE_Morph.prototype.rawOpenCloudDataString = function (str) { var model; - StageMorph.prototype.hiddenPrimitives = {}; - StageMorph.prototype.codeMappings = {}; - StageMorph.prototype.codeHeaders = {}; - StageMorph.prototype.enableCodeMapping = false; - StageMorph.prototype.enableInheritance = true; - StageMorph.prototype.enableSublistIDs = false; - StageMorph.prototype.enablePenLogging = false; - Process.prototype.enableLiveCoding = false; - this.trash = []; - this.hasUnsavedEdits = false; + if (Process.prototype.isCatchingErrors) { try { model = this.serializer.parse(str); @@ -5359,7 +5308,7 @@ IDE_Morph.prototype.openProject = function (name) { } }; -IDE_Morph.prototype.switchToScene = function (scene) { // +++ +IDE_Morph.prototype.switchToScene = function (scene) { var sprites = []; if (!scene || !scene.stage) { return; @@ -5383,6 +5332,21 @@ IDE_Morph.prototype.switchToScene = function (scene) { // +++ this.createCorral(); this.selectSprite(sprites[0] || this.stage); this.fixLayout(); + + // +++ to do: de-globablize settings: + StageMorph.prototype.hiddenPrimitives = {}; + StageMorph.prototype.codeMappings = {}; + StageMorph.prototype.codeHeaders = {}; + StageMorph.prototype.enableCodeMapping = false; + StageMorph.prototype.enableInheritance = true; + StageMorph.prototype.enableSublistIDs = false; + StageMorph.prototype.enablePenLogging = false; + SpriteMorph.prototype.useFlatLineEnds = false; + Process.prototype.enableLiveCoding = false; + Process.prototype.enableHyperOps = true; + this.hasUnsavedEdits = false; + this.trash = []; + this.world().keyboardFocus = this.stage; }; diff --git a/src/objects.js b/src/objects.js index c1ef99b9..a8f30ad7 100644 --- a/src/objects.js +++ b/src/objects.js @@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph, localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph, AlignmentMorph, Process, WorldMap, copyCanvas, useBlurredShadows*/ -modules.objects = '2021-March-17'; +modules.objects = '2021-March-18'; var SpriteMorph; var StageMorph; @@ -7776,6 +7776,7 @@ StageMorph.prototype.init = function (globals) { StageMorph.uber.init.call(this); + this.setExtent(this.dimensions); this.isCachingImage = true; this.cachedHSV = this.color.hsv(); this.acceptsDrops = false; diff --git a/src/scenes.js b/src/scenes.js index 1174385f..78ac0bcf 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -47,9 +47,9 @@ // Global stuff //////////////////////////////////////////////////////// -/*global modules, VariableFrame, aStageMorph*/ +/*global modules, VariableFrame, StageMorph, SpriteMorph*/ -modules.scenes = '2021-March-12'; +modules.scenes = '2021-March-18'; // Scene ///////////////////////////////////////////////////////// @@ -61,8 +61,8 @@ modules.scenes = '2021-March-12'; // Scene instance creation: function Scene(aStageMorph) { - this.name = null; - this.notes = null; + this.name = ''; + this.notes = ''; this.globalVariables = aStageMorph ? aStageMorph.globalVariables() : new VariableFrame(); this.stage = aStageMorph || new StageMorph(this.globalVariables); @@ -71,3 +71,14 @@ function Scene(aStageMorph) { this.sprites = {}; this.targetStage = null; } + +Scene.prototype.addDefaultSprite = function () { + var sprite = new SpriteMorph(this.globalVariables); + sprite.setPosition( + this.stage.center().subtract( + sprite.extent().divideBy(2) + ) + ); + this.stage.add(sprite); + return sprite; +}; From 1230365b416b492216f22aa3a7f99a0ef9377a42 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 18 Mar 2021 16:31:47 +0100 Subject: [PATCH 04/92] more scene refactorings --- src/gui.js | 12 +++++++----- src/objects.js | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/gui.js b/src/gui.js index f9b8cc9b..16178a6b 100644 --- a/src/gui.js +++ b/src/gui.js @@ -2810,7 +2810,7 @@ IDE_Morph.prototype.restore = function () { // IDE_Morph sprite list access -IDE_Morph.prototype.addNewSprite = function () { // +++ sceneify +IDE_Morph.prototype.addNewSprite = function () { var sprite = new SpriteMorph(this.globalVariables), rnd = Process.prototype.reportBasicRandom; @@ -2837,7 +2837,7 @@ IDE_Morph.prototype.addNewSprite = function () { // +++ sceneify this.selectSprite(sprite); }; -IDE_Morph.prototype.paintNewSprite = function () { // +++ sceneify +IDE_Morph.prototype.paintNewSprite = function () { var sprite = new SpriteMorph(this.globalVariables), cos = new Costume(); @@ -2860,7 +2860,7 @@ IDE_Morph.prototype.paintNewSprite = function () { // +++ sceneify ); }; -IDE_Morph.prototype.newCamSprite = function () { // +++ sceneify +IDE_Morph.prototype.newCamSprite = function () { var sprite = new SpriteMorph(this.globalVariables), camDialog; @@ -4577,7 +4577,7 @@ IDE_Morph.prototype.editProjectNotes = function () { text.edit(); }; -IDE_Morph.prototype.newProject = function () { // +++ +IDE_Morph.prototype.newProject = function () { var scene = new Scene(); scene.addDefaultSprite(); @@ -10376,7 +10376,9 @@ CamSnapshotDialogMorph.prototype.ok = function () { this.accept( new Costume( this.videoView.fullImage(), - this.sprite.newCostumeName('camera') + this.sprite.newCostumeName('camera'), + null, + true // no shrink-wrap ).flipped() ); }; diff --git a/src/objects.js b/src/objects.js index a8f30ad7..24d37c77 100644 --- a/src/objects.js +++ b/src/objects.js @@ -10054,7 +10054,8 @@ Costume.prototype.flipped = function () { new Point( this.width() - this.rotationCenter.x, this.rotationCenter.y - ) + ), + true // no shrink-wrap ); return flipped; }; From f4aa21a2a6c911758a9d2dae7497a6da2a5545a6 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 19 Mar 2021 18:01:46 +0100 Subject: [PATCH 05/92] capture global settings in scenes --- HISTORY.md | 3 +++ src/gui.js | 17 +++-------------- src/scenes.js | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/store.js | 20 ++++++++++---------- 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 14cfbbdc..230b0a85 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,9 @@ ## in development: +### 2021-03-19 +* gui, store, scenes: capture global settings in scenes + ## 6.7.3 * **Notable Changes:** * hyperized "key _ pressed?" predicate diff --git a/src/gui.js b/src/gui.js index 03250142..b1c32a29 100644 --- a/src/gui.js +++ b/src/gui.js @@ -5316,6 +5316,7 @@ IDE_Morph.prototype.switchToScene = function (scene) { this.siblings().forEach(morph => morph.destroy() ); + this.scene.captureGlobalSettings(); this.scene = scene; this.projectName = scene.name; this.projectNotes = scene.notes || ''; @@ -5332,21 +5333,9 @@ IDE_Morph.prototype.switchToScene = function (scene) { this.createCorral(); this.selectSprite(sprites[0] || this.stage); this.fixLayout(); - - // +++ to do: de-globablize settings: - StageMorph.prototype.hiddenPrimitives = {}; - StageMorph.prototype.codeMappings = {}; - StageMorph.prototype.codeHeaders = {}; - StageMorph.prototype.enableCodeMapping = false; - StageMorph.prototype.enableInheritance = true; - StageMorph.prototype.enableSublistIDs = false; - StageMorph.prototype.enablePenLogging = false; - SpriteMorph.prototype.useFlatLineEnds = false; - Process.prototype.enableLiveCoding = false; - Process.prototype.enableHyperOps = true; + scene.applyGlobalSettings(); this.hasUnsavedEdits = false; - this.trash = []; - + this.trash = []; // +++ sceneify this.world().keyboardFocus = this.stage; }; diff --git a/src/scenes.js b/src/scenes.js index 78ac0bcf..01e90b83 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -47,9 +47,9 @@ // Global stuff //////////////////////////////////////////////////////// -/*global modules, VariableFrame, StageMorph, SpriteMorph*/ +/*global modules, VariableFrame, StageMorph, SpriteMorph, Process*/ -modules.scenes = '2021-March-18'; +modules.scenes = '2021-March-19'; // Scene ///////////////////////////////////////////////////////// @@ -67,6 +67,20 @@ function Scene(aStageMorph) { aStageMorph.globalVariables() : new VariableFrame(); this.stage = aStageMorph || new StageMorph(this.globalVariables); + // global settings (shared) + this.hiddenPrimitives = {}; + this.codeMappings = {}; + this.codeHeaders = {}; + + // global settings (copied) + this.enableCodeMapping = false; + this.enableInheritance = true; + this.enableSublistIDs = false; + this.enablePenLogging = false; + this.useFlatLineEnds = false; + this.enableLiveCoding = false; + this.enableHyperOps = true; + // for deserializing - do not persist this.sprites = {}; this.targetStage = null; @@ -82,3 +96,29 @@ Scene.prototype.addDefaultSprite = function () { this.stage.add(sprite); return sprite; }; + +Scene.prototype.captureGlobalSettings = function () { + this.hiddenPrimitives = StageMorph.prototype.hiddenPrimitives; + this.codeMappings = StageMorph.prototype.codeMappings; + this.codeHeaders = StageMorph.prototype.codeHeaders; + this.enableCodeMapping = StageMorph.prototype.enableCodeMapping; + this.enableInheritance = StageMorph.prototype.enableInheritance; + this.enableSublistIDs = StageMorph.prototype.enableSublistIDs; + this.enablePenLogging = StageMorph.prototype.enablePenLogging; + this.useFlatLineEnds = SpriteMorph.prototype.useFlatLineEnds; + this.enableLiveCoding = Process.prototype.enableLiveCoding; + this.enableHyperOps = Process.prototype.enableHyperOps; +}; + +Scene.prototype.applyGlobalSettings = function () { + StageMorph.prototype.hiddenPrimitives = this.hiddenPrimitives; + StageMorph.prototype.codeMappings = this.codeMappings; + StageMorph.prototype.codeHeaders = this.codeHeaders; + StageMorph.prototype.enableCodeMapping = this.enableCodeMapping; + StageMorph.prototype.enableInheritance = this.enableInheritance; + StageMorph.prototype.enableSublistIDs = this.enableSublistIDs; + StageMorph.prototype.enablePenLogging = this.enablePenLogging; + SpriteMorph.prototype.useFlatLineEnds = this.useFlatLineEnds; + Process.prototype.enableLiveCoding = this.enableLiveCoding; + Process.prototype.enableHyperOps = this.enableHyperOps; +}; diff --git a/src/store.js b/src/store.js index a8b806cd..feb53b38 100644 --- a/src/store.js +++ b/src/store.js @@ -60,7 +60,7 @@ SyntaxElementMorph, BooleanSlotMorph, normalizeCanvas, contains, Scene*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-March-17'; +modules.store = '2021-March-19'; // XML_Serializer /////////////////////////////////////////////////////// @@ -393,7 +393,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { project.stage.pan = +model.stage.attributes.pan; } if (model.stage.attributes.penlog) { - StageMorph.prototype.enablePenLogging = + project.enablePenLogging = (model.stage.attributes.penlog === 'true'); } @@ -420,19 +420,19 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { Math.max(+model.stage.attributes.height, 180); } project.stage.setExtent(project.stage.dimensions); - SpriteMorph.prototype.useFlatLineEnds = + project.useFlatLineEnds = model.stage.attributes.lines === 'flat'; BooleanSlotMorph.prototype.isTernary = model.stage.attributes.ternary !== 'false'; - Process.prototype.enableHyperOps = + project.enableHyperOps = model.stage.attributes.hyperops !== 'false'; project.stage.isThreadSafe = model.stage.attributes.threadsafe === 'true'; - StageMorph.prototype.enableCodeMapping = + project.enableCodeMapping = model.stage.attributes.codify === 'true'; - StageMorph.prototype.enableInheritance = + project.enableInheritance = model.stage.attributes.inheritance !== 'false'; - StageMorph.prototype.enableSublistIDs = + project.enableSublistIDs = model.stage.attributes.sublistIDs === 'true'; model.hiddenPrimitives = model.project.childNamed('hidden'); @@ -440,7 +440,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { model.hiddenPrimitives.contents.split(' ').forEach( sel => { if (sel) { - StageMorph.prototype.hiddenPrimitives[sel] = true; + project.hiddenPrimitives[sel] = true; } } ); @@ -449,14 +449,14 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { model.codeHeaders = model.project.childNamed('headers'); if (model.codeHeaders) { model.codeHeaders.children.forEach( - xml => StageMorph.prototype.codeHeaders[xml.tag] = xml.contents + xml => project.codeHeaders[xml.tag] = xml.contents ); } model.codeMappings = model.project.childNamed('code'); if (model.codeMappings) { model.codeMappings.children.forEach( - xml => StageMorph.prototype.codeMappings[xml.tag] = xml.contents + xml => project.codeMappings[xml.tag] = xml.contents ); } From 098cea0fc464b80cf3d2bff714168c93cb5a3ac6 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 25 Mar 2021 13:47:45 +0100 Subject: [PATCH 06/92] sceneified trash --- HISTORY.md | 3 +++ snap.html | 4 ++-- src/gui.js | 17 +++++++---------- src/scenes.js | 9 ++++++++- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 230b0a85..2d9bb87e 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,9 @@ ## in development: +### 2021-03-25 +* gui, scenes: sceneified trash + ### 2021-03-19 * gui, store, scenes: capture global settings in scenes diff --git a/snap.html b/snap.html index 8cd4a4c3..0a03db7a 100755 --- a/snap.html +++ b/snap.html @@ -11,8 +11,8 @@ - - + + diff --git a/src/gui.js b/src/gui.js index b1c32a29..a3c96de7 100644 --- a/src/gui.js +++ b/src/gui.js @@ -78,7 +78,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-March-19'; +modules.gui = '2021-March-25'; // Declarations @@ -212,7 +212,7 @@ function IDE_Morph(isAutoFill) { this.init(isAutoFill); } -IDE_Morph.prototype.init = function (isAutoFill) { // +++ +IDE_Morph.prototype.init = function (isAutoFill) { // global font setting MorphicPreferences.globalFontFamily = 'Helvetica, Arial'; @@ -236,8 +236,6 @@ IDE_Morph.prototype.init = function (isAutoFill) { // +++ this.currentCategory = 'motion'; this.currentTab = 'scripts'; - this.trash = []; // deleted sprites - // logoURL is disabled because the image data is hard-copied // to avoid tainting the world canvas // this.logoURL = this.resourceURL('src', 'snap_logo_sm.png'); @@ -3117,7 +3115,7 @@ IDE_Morph.prototype.removeSprite = function (sprite) { this.recordUnsavedChanges(); // remember the deleted sprite so it can be recovered again later - this.trash.push(sprite); + this.scene.trash.push(sprite); }; IDE_Morph.prototype.newSoundName = function (name) { @@ -4015,7 +4013,7 @@ IDE_Morph.prototype.projectMenu = function () { 'Select a sound from the media library' ); - if (this.trash.length) { + if (this.scene.trash.length) { menu.addLine(); menu.addItem( 'Undelete sprites...', @@ -4295,11 +4293,11 @@ IDE_Morph.prototype.undeleteSprites = function (pos) { var menu = new MenuMorph(sprite => this.undelete(sprite, pos), null, this); pos = pos || this.corralBar.bottomRight(); - if (!this.trash.length) { + if (!this.scene.trash.length) { this.showMessage('trash is empty'); return; } - this.trash.forEach(sprite => + this.scene.trash.forEach(sprite => menu.addItem( [ sprite.thumbnail(new Point(24, 24), null, true), // no corpse @@ -4332,7 +4330,7 @@ IDE_Morph.prototype.undelete = function (aSprite, pos) { this.sprites.add(aSprite); this.corral.addSprite(aSprite); this.selectSprite(aSprite); - this.trash = this.trash.filter(sprite => sprite.isCorpse); + this.scene.updateTrash(); } ); }; @@ -5335,7 +5333,6 @@ IDE_Morph.prototype.switchToScene = function (scene) { this.fixLayout(); scene.applyGlobalSettings(); this.hasUnsavedEdits = false; - this.trash = []; // +++ sceneify this.world().keyboardFocus = this.stage; }; diff --git a/src/scenes.js b/src/scenes.js index 01e90b83..9e584fd4 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -49,7 +49,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process*/ -modules.scenes = '2021-March-19'; +modules.scenes = '2021-March-25'; // Scene ///////////////////////////////////////////////////////// @@ -84,6 +84,9 @@ function Scene(aStageMorph) { // for deserializing - do not persist this.sprites = {}; this.targetStage = null; + + // for undeleting sprites - do not persist + this.trash = []; } Scene.prototype.addDefaultSprite = function () { @@ -122,3 +125,7 @@ Scene.prototype.applyGlobalSettings = function () { Process.prototype.enableLiveCoding = this.enableLiveCoding; Process.prototype.enableHyperOps = this.enableHyperOps; }; + +Scene.prototype.updateTrash = function () { + this.trash = this.trash.filter(sprite => sprite.isCorpse); +}; From 6cc5d59ba98522632fed30f67290ed20c676171b Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 25 Mar 2021 17:06:30 +0100 Subject: [PATCH 07/92] first live multi-scene experiment --- HISTORY.md | 1 + src/gui.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2d9bb87e..207638f7 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,6 +4,7 @@ ### 2021-03-25 * gui, scenes: sceneified trash +* gui: first "live" multi-scene experiment ### 2021-03-19 * gui, store, scenes: capture global settings in scenes diff --git a/src/gui.js b/src/gui.js index a3c96de7..4a710cc6 100644 --- a/src/gui.js +++ b/src/gui.js @@ -227,7 +227,12 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.source = null; this.serializer = new SnapSerializer(); - this.scene = new Scene(); + // scenes + this.scenes = [new Scene()]; + this.scene = this.scenes[0]; + this.isAddingScenes = false; // to be factored out + + // editor this.globalVariables = this.scene.globalVariables; this.currentSprite = this.scene.addDefaultSprite(); this.sprites = new List([this.currentSprite]); @@ -3466,6 +3471,14 @@ IDE_Morph.prototype.settingsMenu = function () { 'check to support\nnative JavaScript functions' ); */ + addPreference( + 'Add scenes', + () => this.isAddingScenes = !this.isAddingScenes, + this.isAddingScenes, + 'uncheck to replace the current project,\nwith a new one', + 'check to add other projects,\nto this one', + true + ); if (isRetinaSupported()) { addPreference( 'Retina display support', @@ -3863,6 +3876,9 @@ IDE_Morph.prototype.projectMenu = function () { backup = this.availableBackup(shiftClicked); menu = new MenuMorph(this); + if (this.scenes.length > 1) { + menu.addItem('Scenes...', 'scenesMenu'); + } menu.addItem('Project notes...', 'editProjectNotes'); menu.addLine(); menu.addPair('New', 'createNewProject', '^N'); @@ -4524,6 +4540,28 @@ IDE_Morph.prototype.aboutSnap = function () { dlg.fixLayout(); }; +IDE_Morph.prototype.scenesMenu = function () { + var menu = new MenuMorph(scn => this.switchToScene(scn), null, this), + world = this.world(), + pos = this.controlBar.projectButton.bottomLeft(), + tick = new SymbolMorph( + 'tick', + MorphicPreferences.menuFontSize * 0.75 + ), + empty = tick.fullCopy(); + + empty.render = nop; + this.scenes.forEach(scn => + menu.addItem( + [ + this.scene === scn ? tick : empty, + scn.name + ], + scn + ) + ); + menu.popup(world, pos); +}; IDE_Morph.prototype.editProjectNotes = function () { var dialog = new DialogBoxMorph().withKey('projectNotes'), @@ -4586,7 +4624,7 @@ IDE_Morph.prototype.newProject = function () { if (location.hash.substr(0, 6) !== '#lang:') { location.hash = ''; } - this.switchToScene(scene); + this.openScene(scene); }; IDE_Morph.prototype.save = function () { @@ -5062,14 +5100,14 @@ IDE_Morph.prototype.rawOpenProjectString = function (str) { this.spriteBar.tabBar.tabTo('scripts'); if (Process.prototype.isCatchingErrors) { try { - this.switchToScene( + this.openScene( this.serializer.load(str, this) ); } catch (err) { this.showMessage('Load failed: ' + err); } } else { - this.switchToScene( + this.openScene( this.serializer.load(str, this) ); } @@ -5095,7 +5133,7 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) { try { model = this.serializer.parse(str); this.serializer.loadMediaModel(model.childNamed('media')); - this.switchToScene( + this.openScene( this.serializer.loadProjectModel( model.childNamed('project'), this, @@ -5108,7 +5146,7 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) { } else { model = this.serializer.parse(str); this.serializer.loadMediaModel(model.childNamed('media')); - this.switchToScene( + this.openScene( this.serializer.loadProjectModel( model.childNamed('project'), this, @@ -5306,6 +5344,14 @@ IDE_Morph.prototype.openProject = function (name) { } }; +IDE_Morph.prototype.openScene = function (scene) { + if (!this.isAddingScenes) { + this.scenes = []; + } + this.scenes.push(scene); + this.switchToScene(scene); +}; + IDE_Morph.prototype.switchToScene = function (scene) { var sprites = []; if (!scene || !scene.stage) { From 5ff8b1cbb12377c872ffe7ada17ac2a82b5e78fb Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 30 Mar 2021 14:59:31 +0200 Subject: [PATCH 08/92] added gui documentation --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 207638f7..22159396 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,9 @@ ## in development: +### 2021-03-30 +* gui: added documentation + ### 2021-03-25 * gui, scenes: sceneified trash * gui: first "live" multi-scene experiment diff --git a/snap.html b/snap.html index 0a03db7a..7315d96d 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 4a710cc6..027b5512 100644 --- a/src/gui.js +++ b/src/gui.js @@ -39,10 +39,13 @@ IDE_Morph ProjectDialogMorph + LibraryImportDialogMorph SpriteIconMorph TurtleIconMorph CostumeIconMorph WardrobeMorph + SoundIconMorph + JukeboxMorph StageHandleMorph PaletteHandleMorph CamSnapshotDialogMorph @@ -78,7 +81,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-March-25'; +modules.gui = '2021-March-30'; // Declarations From 8f4025d1c3bd2963267c37451150873ca616f46b Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 30 Mar 2021 17:53:46 +0200 Subject: [PATCH 09/92] added SceneIconMorph and SceneAlbumMorph prototypes --- HISTORY.md | 1 + snap.html | 2 +- src/gui.js | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/scenes.js | 5 +- 4 files changed, 320 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 22159396..95e6a6c2 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,6 +4,7 @@ ### 2021-03-30 * gui: added documentation +* gui: added SceneIconMorph and SceneAlbumMorph prototypes ### 2021-03-25 * gui, scenes: sceneified trash diff --git a/snap.html b/snap.html index 7315d96d..98eaca31 100755 --- a/snap.html +++ b/snap.html @@ -11,7 +11,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 027b5512..b2713647 100644 --- a/src/gui.js +++ b/src/gui.js @@ -46,6 +46,8 @@ WardrobeMorph SoundIconMorph JukeboxMorph + SceneIconMorph + SceneAlbumMorph StageHandleMorph PaletteHandleMorph CamSnapshotDialogMorph @@ -94,6 +96,8 @@ var TurtleIconMorph; var WardrobeMorph; var SoundIconMorph; var JukeboxMorph; +var SceneIconMorph; +var SceneAlbumMorph; var StageHandleMorph; var PaletteHandleMorph; var CamSnapshotDialogMorph; @@ -10029,6 +10033,316 @@ JukeboxMorph.prototype.reactToDropOf = function (icon) { this.updateList(); }; +// SceneIconMorph //////////////////////////////////////////////////// + +/* + I am a selectable element in a SceneAlbum, keeping + a self-updating thumbnail of the scene I'm respresenting, and a + self-updating label of the scene's name (in case it is changed + elsewhere) +*/ + +// SceneIconMorph inherits from ToggleButtonMorph (Widgets) +// ... and copies methods from SpriteIconMorph + +SceneIconMorph.prototype = new ToggleButtonMorph(); +SceneIconMorph.prototype.constructor = SceneIconMorph; +SceneIconMorph.uber = ToggleButtonMorph.prototype; + +// SceneIconMorph settings + +SceneIconMorph.prototype.thumbSize = new Point(80, 60); +SceneIconMorph.prototype.labelShadowOffset = null; +SceneIconMorph.prototype.labelShadowColor = null; +SceneIconMorph.prototype.labelColor = WHITE; +SceneIconMorph.prototype.fontSize = 9; + +// SceneIconMorph instance creation: + +function SceneIconMorph(aScene) { + this.init(aScene); +} + +SceneIconMorph.prototype.init = function (aScene) { + var colors, action, query; + + colors = [ + IDE_Morph.prototype.groupColor, + IDE_Morph.prototype.frameColor, + IDE_Morph.prototype.frameColor + ]; + + action = () => { + // make my scene the current one + var ide = this.parentThatIsA(IDE_Morph), + album = this.parentThatIsA(SceneAlbumMorph); + + if (ide) { + ide.switchToScene(this.object); + } + if (album) { + album.updateSelection(); + } + }; + + query = () => { + // answer true if my scene is the current one + var ide = this.parentThatIsA(IDE_Morph); + + if (ide) { + return ide.scene === this.object; + } + return false; + }; + + // additional properties: + this.object = aScene || new Scene(); // mandatory, actually + this.version = this.object.version; + this.thumbnail = null; + + // initialize inherited properties: + SceneIconMorph.uber.init.call( + this, + colors, // color overrides, : [normal, highlight, pressed] + null, // target - not needed here + action, // a toggle function + this.object.name, // label string + query, // predicate/selector + null, // environment + null // hint + ); + + // override defaults and build additional components + this.isDraggable = true; + this.createThumbnail(); + this.padding = 2; + this.corner = 8; + this.fixLayout(); + this.fps = 1; +}; + +SceneIconMorph.prototype.createThumbnail = + SpriteIconMorph.prototype.createThumbnail; // +++ +/* + if (this.thumbnail) { + this.thumbnail.destroy(); + } + + this.thumbnail = new Morph(); + this.thumbnail.isCachingImage = true; + this.thumbnail.bounds.setExtent(this.thumbSize); + if (this.object instanceof SpriteMorph) { // support nested sprites + this.thumbnail.cachedImage = this.object.fullThumbnail( + this.thumbSize, + this.thumbnail.cachedImage + ); + this.add(this.thumbnail); + this.createRotationButton(); + } else { + this.thumbnail.cachedImage = this.object.thumbnail( + this.thumbSize, + this.thumbnail.cachedImage + ); + this.add(this.thumbnail); + } +*/ + +SceneIconMorph.prototype.createLabel + = SpriteIconMorph.prototype.createLabel; + +// SceneIconMorph stepping + +SceneIconMorph.prototype.step + = SpriteIconMorph.prototype.step; + +// SceneIconMorph layout + +SceneIconMorph.prototype.fixLayout + = SpriteIconMorph.prototype.fixLayout; + +// SceneIconMorph menu + +SceneIconMorph.prototype.userMenu = function () { + var menu = new MenuMorph(this), + ide = this.parentThatIsA(IDE_Morph); + if (!(this.object instanceof Scene)) {return null; } + menu.addItem("rename", "renameScene"); + if (ide.scenes.length > 1) { + menu.addItem("delete", "removeScene"); + } + // menu.addItem("export", "exportScene"); + return menu; +}; + +SceneIconMorph.prototype.renameScene = function () { + var scene = this.object, + album = this.parentThatIsA(SceneAlbumMorph), + ide = this.parentThatIsA(IDE_Morph); + new DialogBoxMorph( + null, + answer => { + if (answer && (answer !== scene.name)) { + scene.name = album.scene.newSceneName( + answer, + scene + ); + scene.version = Date.now(); + ide.recordUnsavedChanges(); + } + } + ).prompt( + 'rename scene', + scene.name, + this.world() + ); +}; + +SceneIconMorph.prototype.removeScene = function () { + var album = this.parentThatIsA(SceneAlbumMorph), + idx = this.parent.children.indexOf(this), + off = 0, // 2, + ide = this.parentThatIsA(IDE_Morph); + album.removeSceneAt(idx - off); // ignore buttons + if (ide.scene === this.object) { + ide.switchToScene(ide.scenes[0]); + } +}; + +SceneIconMorph.prototype.exportScene = function () { + // under construction + var ide = this.parentThatIsA(IDE_Morph); + ide.saveFileAs(this.object.contents.src, 'text/svg', this.object.name); +}; + +// SceneIconMorph drawing + +SceneIconMorph.prototype.render + = SpriteIconMorph.prototype.render; + +// SceneIconMorph drag & drop + +SceneIconMorph.prototype.rootForGrab = function () { + // +++ to do: make sure scene icons can only be grabbed if there are + // more than 1 + return this; +}; + +SceneIconMorph.prototype.prepareToBeGrabbed = function () { + this.mouseClickLeft(); // select me + this.removeScene(); +}; + +// SceneAlbumMorph /////////////////////////////////////////////////////// + +// I am a watcher on a project's scenes list // +++ to do: make scenes a list + +// SceneAlbumMorph inherits from ScrollFrameMorph + +SceneAlbumMorph.prototype = new ScrollFrameMorph(); +SceneAlbumMorph.prototype.constructor = SceneAlbumMorph; +SceneAlbumMorph.uber = ScrollFrameMorph.prototype; + +// SceneAlbumMorph instance creation: + +function SceneAlbumMorph(anIDE, sliderColor) { + this.init(anIDE, sliderColor); +} + +SceneAlbumMorph.prototype.init = function (anIDE, sliderColor) { + // additional properties + this.ide = anIDE; + this.scene = anIDE.scene; + this.version = null; + + // initialize inherited properties + SceneAlbumMorph.uber.init.call(this, null, null, sliderColor); + + // configure inherited properties + this.fps = 2; + this.updateList(); + this.updateSelection(); +}; + +// SceneAlbumMorph updating + +SceneAlbumMorph.prototype.updateList = function () { + var x = this.left() + 5, + y = this.top() + 5, + padding = 4, + oldPos = this.contents.position(), + icon; + + this.changed(); + + this.contents.destroy(); + this.contents = new FrameMorph(this); + this.contents.acceptsDrops = false; + this.contents.reactToDropOf = (icon) => { + this.reactToDropOf(icon); + }; + this.addBack(this.contents); + + this.ide.scenes.asArray().forEach(scene => { + icon = new SceneIconMorph(scene); + icon.setPosition(new Point(x, y)); + this.addContents(icon); + y = icon.bottom() + padding; + }); + this.version = this.ide.scenes.lastChanged; + + this.contents.setPosition(oldPos); + this.adjustScrollBars(); + this.changed(); + + this.updateSelection(); +}; + +SceneAlbumMorph.prototype.updateSelection = function () { + this.contents.children.forEach(function (morph) { + if (morph.refresh) {morph.refresh(); } + }); + this.scene = this.ide.scene; +}; + +// SceneAlbumMorph stepping + +SceneAlbumMorph.prototype.step = function () { + if (this.version !== this.ide.scenes.lastChanged) { + this.updateList(); + } + if (this.scene !== this.ide.scene) { + this.updateSelection(); + } +}; + +// Wardrobe ops + +SceneAlbumMorph.prototype.removeSceneAt = function (idx) { + this.ide.scenes.remove(idx); + this.updateList(); +}; + +// SceneAlbumMorph drag & drop + +SceneAlbumMorph.prototype.wantsDropOf = function (morph) { + return morph instanceof SceneIconMorph; +}; + +SceneAlbumMorph.prototype.reactToDropOf = function (icon) { + var idx = 0, + scene = icon.object, + top = icon.top(); + icon.destroy(); + this.contents.children.forEach(item => { + if (item instanceof SceneIconMorph && item.top() < top - 4) { + idx += 1; + } + }); + this.ide.scenes.add(scene, idx + 1); + this.updateList(); + icon.mouseClickLeft(); // select +}; + // StageHandleMorph //////////////////////////////////////////////////////// // I am a horizontal resizing handle for a StageMorph diff --git a/src/scenes.js b/src/scenes.js index 9e584fd4..d1f8a039 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -49,7 +49,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process*/ -modules.scenes = '2021-March-25'; +modules.scenes = '2021-March-30'; // Scene ///////////////////////////////////////////////////////// @@ -87,6 +87,9 @@ function Scene(aStageMorph) { // for undeleting sprites - do not persist this.trash = []; + + // for observer optimization - do not persist + this.version = Date.now(); } Scene.prototype.addDefaultSprite = function () { From 7d7b38156956cc2c50f6bae1a76bcc362513ed1c Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 30 Mar 2021 19:29:48 +0200 Subject: [PATCH 10/92] turned scenes into an observable list --- HISTORY.md | 1 + src/gui.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 95e6a6c2..af3caf86 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,6 +5,7 @@ ### 2021-03-30 * gui: added documentation * gui: added SceneIconMorph and SceneAlbumMorph prototypes +* gui: turned scenes into an observable list ### 2021-03-25 * gui, scenes: sceneified trash diff --git a/src/gui.js b/src/gui.js index b2713647..3f8c81d0 100644 --- a/src/gui.js +++ b/src/gui.js @@ -235,8 +235,8 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.serializer = new SnapSerializer(); // scenes - this.scenes = [new Scene()]; - this.scene = this.scenes[0]; + this.scenes = new List([new Scene()]); + this.scene = this.scenes.at(1); this.isAddingScenes = false; // to be factored out // editor @@ -3883,7 +3883,7 @@ IDE_Morph.prototype.projectMenu = function () { backup = this.availableBackup(shiftClicked); menu = new MenuMorph(this); - if (this.scenes.length > 1) { + if (this.scenes.length() > 1) { menu.addItem('Scenes...', 'scenesMenu'); } menu.addItem('Project notes...', 'editProjectNotes'); @@ -4558,7 +4558,7 @@ IDE_Morph.prototype.scenesMenu = function () { empty = tick.fullCopy(); empty.render = nop; - this.scenes.forEach(scn => + this.scenes.asArray().forEach(scn => menu.addItem( [ this.scene === scn ? tick : empty, @@ -5353,9 +5353,9 @@ IDE_Morph.prototype.openProject = function (name) { IDE_Morph.prototype.openScene = function (scene) { if (!this.isAddingScenes) { - this.scenes = []; + this.scenes = new List(); } - this.scenes.push(scene); + this.scenes.add(scene); this.switchToScene(scene); }; @@ -10167,7 +10167,7 @@ SceneIconMorph.prototype.userMenu = function () { ide = this.parentThatIsA(IDE_Morph); if (!(this.object instanceof Scene)) {return null; } menu.addItem("rename", "renameScene"); - if (ide.scenes.length > 1) { + if (ide.scenes.length() > 1) { menu.addItem("delete", "removeScene"); } // menu.addItem("export", "exportScene"); @@ -10204,7 +10204,7 @@ SceneIconMorph.prototype.removeScene = function () { ide = this.parentThatIsA(IDE_Morph); album.removeSceneAt(idx - off); // ignore buttons if (ide.scene === this.object) { - ide.switchToScene(ide.scenes[0]); + ide.switchToScene(ide.scenes.at(1)); } }; From 85faa08f83dad95c91a12726a13514430935294c Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 30 Mar 2021 19:56:33 +0200 Subject: [PATCH 11/92] added scene icon thumbnails --- HISTORY.md | 1 + src/gui.js | 27 ++++++++------------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index af3caf86..eb36b98e 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,7 @@ * gui: added documentation * gui: added SceneIconMorph and SceneAlbumMorph prototypes * gui: turned scenes into an observable list +* gui: added scene icon thumbnails ### 2021-03-25 * gui, scenes: sceneified trash diff --git a/src/gui.js b/src/gui.js index 3f8c81d0..928cac1c 100644 --- a/src/gui.js +++ b/src/gui.js @@ -10121,9 +10121,7 @@ SceneIconMorph.prototype.init = function (aScene) { this.fps = 1; }; -SceneIconMorph.prototype.createThumbnail = - SpriteIconMorph.prototype.createThumbnail; // +++ -/* +SceneIconMorph.prototype.createThumbnail = function () { if (this.thumbnail) { this.thumbnail.destroy(); } @@ -10131,21 +10129,12 @@ SceneIconMorph.prototype.createThumbnail = this.thumbnail = new Morph(); this.thumbnail.isCachingImage = true; this.thumbnail.bounds.setExtent(this.thumbSize); - if (this.object instanceof SpriteMorph) { // support nested sprites - this.thumbnail.cachedImage = this.object.fullThumbnail( - this.thumbSize, - this.thumbnail.cachedImage - ); - this.add(this.thumbnail); - this.createRotationButton(); - } else { - this.thumbnail.cachedImage = this.object.thumbnail( - this.thumbSize, - this.thumbnail.cachedImage - ); - this.add(this.thumbnail); - } -*/ + this.thumbnail.cachedImage = this.object.stage.thumbnail( + this.thumbSize, + this.thumbnail.cachedImage + ); + this.add(this.thumbnail); +}; SceneIconMorph.prototype.createLabel = SpriteIconMorph.prototype.createLabel; @@ -10234,7 +10223,7 @@ SceneIconMorph.prototype.prepareToBeGrabbed = function () { // SceneAlbumMorph /////////////////////////////////////////////////////// -// I am a watcher on a project's scenes list // +++ to do: make scenes a list +// I am a watcher on a project's scenes list // SceneAlbumMorph inherits from ScrollFrameMorph From eb55779b2e6bb726874a052fe1d9958463d34a19 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 31 Mar 2021 10:29:19 +0200 Subject: [PATCH 12/92] tweaked scene icon settings --- snap.html | 2 +- src/gui.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/snap.html b/snap.html index 98eaca31..23d9003c 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 928cac1c..7f249d95 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-March-30'; +modules.gui = '2021-March-31'; // Declarations @@ -151,6 +151,8 @@ IDE_Morph.prototype.setDefaultDesign = function () { = IDE_Morph.prototype.buttonLabelColor; TurtleIconMorph.prototype.labelColor = IDE_Morph.prototype.buttonLabelColor; + SceneIconMorph.prototype.labelColor + = IDE_Morph.prototype.buttonLabelColor; SyntaxElementMorph.prototype.contrast = 65; ScriptsMorph.prototype.feedbackColor = WHITE; @@ -190,6 +192,8 @@ IDE_Morph.prototype.setFlatDesign = function () { = IDE_Morph.prototype.buttonLabelColor; TurtleIconMorph.prototype.labelColor = IDE_Morph.prototype.buttonLabelColor; + SceneIconMorph.prototype.labelColor + = IDE_Morph.prototype.buttonLabelColor; SyntaxElementMorph.prototype.contrast = 25; ScriptsMorph.prototype.feedbackColor = new Color(153, 255, 213); @@ -10051,7 +10055,7 @@ SceneIconMorph.uber = ToggleButtonMorph.prototype; // SceneIconMorph settings -SceneIconMorph.prototype.thumbSize = new Point(80, 60); +SceneIconMorph.prototype.thumbSize = new Point(40, 40); // (80, 60); SceneIconMorph.prototype.labelShadowOffset = null; SceneIconMorph.prototype.labelShadowColor = null; SceneIconMorph.prototype.labelColor = WHITE; From b3c7b4dc12caa860b37492a6bb9edc917eb41ddd Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 31 Mar 2021 11:24:38 +0200 Subject: [PATCH 13/92] moved stage icon to the top of the corral --- HISTORY.md | 4 ++++ src/gui.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index eb36b98e..2db7a028 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,10 @@ ## in development: +### 2021-03-31 +* gui: tweaked scene icon settings +* gui: moved stage icon to the top of the corral + ### 2021-03-30 * gui: added documentation * gui: added SceneIconMorph and SceneAlbumMorph prototypes diff --git a/src/gui.js b/src/gui.js index 7f249d95..fcacb45a 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1920,7 +1920,8 @@ IDE_Morph.prototype.createCorral = function () { this.corral.add(frame); this.corral.fixLayout = function () { - this.stageIcon.setCenter(this.center()); + // this.stageIcon.setCenter(this.center()); // version before scenes + this.stageIcon.setTop(this.top()); this.stageIcon.setLeft(this.left() + padding); this.frame.setLeft(this.stageIcon.right() + padding); this.frame.setExtent(new Point( From 7197d226b0f1ffe7b75d4274c237ee6a4259335c Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 31 Mar 2021 17:22:36 +0200 Subject: [PATCH 14/92] tweaked scene icons --- src/gui.js | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/gui.js b/src/gui.js index fcacb45a..2542c4aa 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1919,10 +1919,22 @@ IDE_Morph.prototype.createCorral = function () { this.corral.frame = frame; this.corral.add(frame); + // scenes +++ + this.corral.album = new SceneAlbumMorph(this, this.sliderColor); + this.corral.album.color = this.groupColor; // +++ this.frameColor; + this.corral.add(this.corral.album); + this.corral.fixLayout = function () { // this.stageIcon.setCenter(this.center()); // version before scenes this.stageIcon.setTop(this.top()); this.stageIcon.setLeft(this.left() + padding); + + // scenes +++ + this.album.setLeft(this.left()); + this.album.setTop(this.stageIcon.bottom() + padding); + this.album.setWidth(this.stageIcon.width() + padding * 2); + this.album.setHeight(this.height() - this.stageIcon.height() - padding); + this.frame.setLeft(this.stageIcon.right() + padding); this.frame.setExtent(new Point( this.right() - this.frame.left(), @@ -10056,7 +10068,7 @@ SceneIconMorph.uber = ToggleButtonMorph.prototype; // SceneIconMorph settings -SceneIconMorph.prototype.thumbSize = new Point(40, 40); // (80, 60); +SceneIconMorph.prototype.thumbSize = new Point(40, 30); // +++ (40, 40), (80, 60); SceneIconMorph.prototype.labelShadowOffset = null; SceneIconMorph.prototype.labelShadowColor = null; SceneIconMorph.prototype.labelColor = WHITE; @@ -10093,7 +10105,6 @@ SceneIconMorph.prototype.init = function (aScene) { query = () => { // answer true if my scene is the current one var ide = this.parentThatIsA(IDE_Morph); - if (ide) { return ide.scene === this.object; } @@ -10111,7 +10122,7 @@ SceneIconMorph.prototype.init = function (aScene) { colors, // color overrides, : [normal, highlight, pressed] null, // target - not needed here action, // a toggle function - this.object.name, // label string + this.object.name || localize('untitled'), // label string // +++ query, // predicate/selector null, // environment null // hint @@ -10141,8 +10152,37 @@ SceneIconMorph.prototype.createThumbnail = function () { this.add(this.thumbnail); }; +/* +++ SceneIconMorph.prototype.createLabel = SpriteIconMorph.prototype.createLabel; +*/ + +SceneIconMorph.prototype.createLabel = function () { // +++ + var txt; + + if (this.label) { + this.label.destroy(); + } + txt = new StringMorph( + this.object.name || localize('untitled'), + this.fontSize, + this.fontStyle, + false, // true + false, + false, + this.labelShadowOffset, + this.labelShadowColor, + this.labelColor + ); + + this.label = new FrameMorph(); + this.label.acceptsDrops = false; + this.label.alpha = 0; + this.label.setExtent(txt.extent()); + txt.setPosition(this.label.position()); + this.label.add(txt); + this.add(this.label); +}; // SceneIconMorph stepping From 5c830680d6fdc4685cbe5f2d0f8c32e64ecd503a Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 1 Apr 2021 11:14:16 +0200 Subject: [PATCH 15/92] made scene icons selectable --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 18 +++++++----------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2db7a028..c5b6be9f 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,9 @@ ## in development: +### 2021-04-01 +* gui: made scene icons selectable + ### 2021-03-31 * gui: tweaked scene icon settings * gui: moved stage icon to the top of the corral diff --git a/snap.html b/snap.html index 23d9003c..04dee60c 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 2542c4aa..4a528ff4 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-March-31'; +modules.gui = '2021-April-1'; // Declarations @@ -10093,20 +10093,16 @@ SceneIconMorph.prototype.init = function (aScene) { // make my scene the current one var ide = this.parentThatIsA(IDE_Morph), album = this.parentThatIsA(SceneAlbumMorph); - - if (ide) { - ide.switchToScene(this.object); - } - if (album) { - album.updateSelection(); - } + album.scene = this.object; + ide.switchToScene(this.object); + album.updateSelection(); }; query = () => { // answer true if my scene is the current one - var ide = this.parentThatIsA(IDE_Morph); - if (ide) { - return ide.scene === this.object; + var album = this.parentThatIsA(SceneAlbumMorph); + if (album) { + return album.scene === this.object; } return false; }; From d79b0a70fa84816b6110333a51e5355fd209a7df Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 1 Apr 2021 11:49:45 +0200 Subject: [PATCH 16/92] made scene icons observe the scene's stage versions --- HISTORY.md | 1 + src/gui.js | 15 +++++++++++---- src/scenes.js | 3 --- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c5b6be9f..8e3ef607 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,6 +4,7 @@ ### 2021-04-01 * gui: made scene icons selectable +* gui: made scene icons observe the scene's stage versions ### 2021-03-31 * gui: tweaked scene icon settings diff --git a/src/gui.js b/src/gui.js index 4a528ff4..8e3f7230 100644 --- a/src/gui.js +++ b/src/gui.js @@ -10109,7 +10109,7 @@ SceneIconMorph.prototype.init = function (aScene) { // additional properties: this.object = aScene || new Scene(); // mandatory, actually - this.version = this.object.version; + this.version = this.object.stage.version; this.thumbnail = null; // initialize inherited properties: @@ -10182,8 +10182,15 @@ SceneIconMorph.prototype.createLabel = function () { // +++ // SceneIconMorph stepping -SceneIconMorph.prototype.step - = SpriteIconMorph.prototype.step; +SceneIconMorph.prototype.step = function () { + if (this.version !== this.object.stage.version) { + this.createThumbnail(); + this.createLabel(); + this.fixLayout(); + this.version = this.object.stage.version; + this.refresh(); + } +}; // SceneIconMorph layout @@ -10216,7 +10223,7 @@ SceneIconMorph.prototype.renameScene = function () { answer, scene ); - scene.version = Date.now(); + scene.stage.version = Date.now(); // +++ also do this in other places ide.recordUnsavedChanges(); } } diff --git a/src/scenes.js b/src/scenes.js index d1f8a039..1dd96419 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -87,9 +87,6 @@ function Scene(aStageMorph) { // for undeleting sprites - do not persist this.trash = []; - - // for observer optimization - do not persist - this.version = Date.now(); } Scene.prototype.addDefaultSprite = function () { From a28244a9763a429a44931ba881be2ded84d82db9 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 2 Apr 2021 11:14:54 +0200 Subject: [PATCH 17/92] made scrollbars in the wardrobe and jukebox more responsive --- HISTORY.md | 19 ++++++++++++++----- snap.html | 2 +- src/gui.js | 8 ++++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 8d3e3b46..1adb0ecf 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,19 @@ # Snap! (BYOB) History -## in development: +## in development for v7: +* **New Features:** + * Scenes +* **Notable Fixes:** + * made scrollbars in the wardrobe and jukebox more responsive + +### 2021-04-01 +* gui: made scrollbars in the wardrobe and jukebox more responsive + +### 2021-04-01 +* gui: made scene icons selectable +* gui: made scene icons observe the scene's stage versions + +## 6.7.4 dev: * **Notable Fixes:** * fixed DEAL in the APL library, thanks, Brian! * fixed a resizing edge case bug for the stage prompter (ASK command) @@ -10,10 +23,6 @@ * fixed DEAL in the APL library, thanks, Brian! * objects: fixed a resizing edge case bug for the stage prompter (ASK command) -### 2021-04-01 -* gui: made scene icons selectable -* gui: made scene icons observe the scene's stage versions - ### 2021-03-31 * gui: tweaked scene icon settings * gui: moved stage icon to the top of the corral diff --git a/snap.html b/snap.html index 8a6f3f79..1d37936d 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 8e3f7230..6262838f 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-1'; +modules.gui = '2021-April-2'; // Declarations @@ -9469,7 +9469,7 @@ WardrobeMorph.prototype.init = function (aSprite, sliderColor) { WardrobeMorph.uber.init.call(this, null, null, sliderColor); // configure inherited properties - this.fps = 2; + // this.fps = 2; // commented out to make scrollbars more responsive this.updateList(); }; @@ -9935,7 +9935,7 @@ JukeboxMorph.prototype.init = function (aSprite, sliderColor) { // configure inherited properties this.acceptsDrops = false; - this.fps = 2; + // this.fps = 2; // commented out to make scrollbars more responsive this.updateList(); }; @@ -10295,7 +10295,7 @@ SceneAlbumMorph.prototype.init = function (anIDE, sliderColor) { SceneAlbumMorph.uber.init.call(this, null, null, sliderColor); // configure inherited properties - this.fps = 2; + // this.fps = 2; // commented out to make scrollbars more responsive this.updateList(); this.updateSelection(); }; From d31a3b81650afec04bb9bd2d369b7c9418cae859 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 8 Apr 2021 19:07:03 +0200 Subject: [PATCH 18/92] scroll selected scene icon into view --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 41 +++++++++++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 1adb0ecf..0181a7c2 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-08 +* gui: scroll selected scene icon into view + ### 2021-04-01 * gui: made scrollbars in the wardrobe and jukebox more responsive diff --git a/snap.html b/snap.html index 1d37936d..b97612d9 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 6262838f..fe9c64fe 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-2'; +modules.gui = '2021-April-8'; // Declarations @@ -1878,9 +1878,10 @@ IDE_Morph.prototype.createCorralBar = function () { }; }; -IDE_Morph.prototype.createCorral = function () { +IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { // assumes the corral bar has already been created - var frame, padding = 5, myself = this; + var frame, padding = 5, myself = this, + album = this.corral? this.corral.album : null; this.createStageHandle(); this.createPaletteHandle(); @@ -1920,7 +1921,8 @@ IDE_Morph.prototype.createCorral = function () { this.corral.add(frame); // scenes +++ - this.corral.album = new SceneAlbumMorph(this, this.sliderColor); + this.corral.album = keepSceneAlbum ? album + : new SceneAlbumMorph(this, this.sliderColor); this.corral.album.color = this.groupColor; // +++ this.frameColor; this.corral.add(this.corral.album); @@ -1988,7 +1990,7 @@ IDE_Morph.prototype.createCorral = function () { } }); myself.sprites.add(spriteIcon.object, idx); - myself.createCorral(); + myself.createCorral(true); // keep scenes myself.fixLayout(); }; }; @@ -3133,7 +3135,7 @@ IDE_Morph.prototype.removeSprite = function (sprite) { if (idx > 0) { this.sprites.remove(idx); } - this.createCorral(); + this.createCorral(true); // keep scenes this.fixLayout(); this.currentSprite = detect( this.stage.children, @@ -5373,10 +5375,10 @@ IDE_Morph.prototype.openScene = function (scene) { this.scenes = new List(); } this.scenes.add(scene); - this.switchToScene(scene); + this.switchToScene(scene, this.isAddingScenes); }; -IDE_Morph.prototype.switchToScene = function (scene) { +IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { var sprites = []; if (!scene || !scene.stage) { return; @@ -5398,9 +5400,15 @@ IDE_Morph.prototype.switchToScene = function (scene) { sprites.sort((x, y) => x.idx - y.idx); this.sprites = new List(sprites); this.stage.pauseGenericHatBlocks(); - this.createCorral(); + this.createCorral(!refreshAlbum); // keep scenes this.selectSprite(sprites[0] || this.stage); this.fixLayout(); + this.corral.album.updateSelection(); + this.corral.album.contents.children.forEach(function (morph) { + if (morph.state) { + morph.scrollIntoView(); + } + }); scene.applyGlobalSettings(); this.hasUnsavedEdits = false; this.world().keyboardFocus = this.stage; @@ -8899,7 +8907,7 @@ SpriteIconMorph.prototype.prepareToBeGrabbed = function () { if (ide) { idx = ide.sprites.asArray().indexOf(this.object); ide.sprites.remove(idx + 1); - ide.createCorral(); + ide.createCorral(true); // keep scenes ide.fixLayout(); } }; @@ -9591,7 +9599,9 @@ WardrobeMorph.prototype.updateList = function () { WardrobeMorph.prototype.updateSelection = function () { this.contents.children.forEach(function (morph) { - if (morph.refresh) {morph.refresh(); } + if (morph.refresh) { + morph.refresh(); + } }); this.spriteVersion = this.sprite.version; }; @@ -10004,7 +10014,9 @@ JukeboxMorph.prototype.updateList = function () { JukeboxMorph.prototype.updateSelection = function () { this.contents.children.forEach(morph => { - if (morph.refresh) {morph.refresh(); } + if (morph.refresh) { + morph.refresh(); + } }); this.spriteVersion = this.sprite.version; }; @@ -10095,7 +10107,6 @@ SceneIconMorph.prototype.init = function (aScene) { album = this.parentThatIsA(SceneAlbumMorph); album.scene = this.object; ide.switchToScene(this.object); - album.updateSelection(); }; query = () => { @@ -10336,7 +10347,9 @@ SceneAlbumMorph.prototype.updateList = function () { SceneAlbumMorph.prototype.updateSelection = function () { this.contents.children.forEach(function (morph) { - if (morph.refresh) {morph.refresh(); } + if (morph.refresh) { + morph.refresh(); + } }); this.scene = this.ide.scene; }; From 01f3108bddab94cca917ca446c67e1396f666e1d Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 12 Apr 2021 10:46:45 +0200 Subject: [PATCH 19/92] new "switch to scene _" command primitive --- HISTORY.md | 3 +++ snap.html | 8 ++++---- src/blocks.js | 30 +++++++++++++++++++++++++++-- src/gui.js | 4 ++-- src/objects.js | 12 +++++++++++- src/threads.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 98 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index d73ed8ff..660eaf0c 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-12 +* blocks, objects, threads, gui: new "switch to scene _" command primitive + ### 2021-04-08 * gui: scroll selected scene icon into view diff --git a/snap.html b/snap.html index 25e782d1..a64ff8ad 100755 --- a/snap.html +++ b/snap.html @@ -8,11 +8,11 @@ - - - + + + - + diff --git a/src/blocks.js b/src/blocks.js index ee142fa9..2e035ecc 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -158,7 +158,7 @@ CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2021-February-27'; +modules.blocks = '2021-April-12'; var SyntaxElementMorph; var BlockMorph; @@ -748,6 +748,11 @@ SyntaxElementMorph.prototype.labelParts = { 'parameters' : ['parameters'] } }, + '%scn': { + type: 'input', + tags: 'read-only', + menu: 'scenesMenu' + }, // video @@ -2455,6 +2460,7 @@ BlockSymbolMorph.prototype.getShadowRenderColor = function () { %r - round reporter slot %p - hexagonal predicate slot %vid - chameleon colored rectangular drop-down for video modes + %scn - chameleon colored rectangular drop-down for scene names rings: @@ -9728,7 +9734,7 @@ InputSlotMorph.prototype.audioMenu = function (searching) { 'spectrum' : ['spectrum'], 'resolution' : ['resolution'] }; - if (searching) {return {}; } + if (searching) {return dict; } if (this.world().currentKey === 16) { // shift dict['~'] = null; @@ -9738,6 +9744,26 @@ InputSlotMorph.prototype.audioMenu = function (searching) { return dict; }; +InputSlotMorph.prototype.scenesMenu = function (searching) { + var scenes = this.parentThatIsA(IDE_Morph).scenes, + dict = {}; + + if (!searching && scenes.length() > 1) { + scenes.itemsArray().forEach(scn => { + if (scn.name) { + dict[scn.name] = scn.name; + } + }); + } + dict['~'] = null; + dict.next = ['next']; + dict.previous = ['previous']; + dict['1 '] = 1; // trailing space needed to prevent undesired sorting + dict.last = ['last']; + dict.random = ['random']; + return dict; +}; + InputSlotMorph.prototype.setChoices = function (dict, readonly) { // externally specify choices and read-only status, // used for custom blocks diff --git a/src/gui.js b/src/gui.js index d3ff8023..6c663f3b 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-09'; +modules.gui = '2021-April-12'; // Declarations @@ -10346,12 +10346,12 @@ SceneAlbumMorph.prototype.updateList = function () { }; SceneAlbumMorph.prototype.updateSelection = function () { + this.scene = this.ide.scene; this.contents.children.forEach(function (morph) { if (morph.refresh) { morph.refresh(); } }); - this.scene = this.ide.scene; }; // SceneAlbumMorph stepping diff --git a/src/objects.js b/src/objects.js index 130d77ee..593443c4 100644 --- a/src/objects.js +++ b/src/objects.js @@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph, localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph, AlignmentMorph, Process, WorldMap, copyCanvas, useBlurredShadows*/ -modules.objects = '2021-April-09'; +modules.objects = '2021-April-12'; var SpriteMorph; var StageMorph; @@ -426,6 +426,12 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'go back %n layers', defaults: [1] }, + doSwitchToScene: { + type: 'command', + category: 'looks', + spec: 'switch to scene %scn', + defaults: [['next']] + }, // Looks - Debugging primitives for development mode doScreenshot: { @@ -2414,6 +2420,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('goToLayer')); blocks.push(block('goBack')); + blocks.push('-'); + blocks.push(block('doSwitchToScene')); // for debugging: /////////////// @@ -8617,6 +8625,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('hide')); blocks.push(watcherToggle('reportShown')); blocks.push(block('reportShown')); + blocks.push('-'); + blocks.push(block('doSwitchToScene')); // for debugging: /////////////// diff --git a/src/threads.js b/src/threads.js index 7f615bac..b4d9a5d2 100644 --- a/src/threads.js +++ b/src/threads.js @@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy, Map, isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, BLACK, TableFrameMorph, ColorSlotMorph, isSnapObject, newCanvas, Symbol, SVG_Costume*/ -modules.threads = '2021-March-19'; +modules.threads = '2021-April-12'; var ThreadManager; var Process; @@ -4608,6 +4608,55 @@ Process.prototype.goToLayer = function (name) { } }; +// Process scene primitives + +Process.prototype.doSwitchToScene = function (id) { + var rcvr = this.blockReceiver(), + idx = 0, + ide, scenes, num, scene; + + this.assertAlive(rcvr); + ide = rcvr.parentThatIsA(IDE_Morph); + scenes = ide.scenes; + + if (id instanceof Array) { // special named indices + switch (this.inputOption(id)) { + case 'next': + idx = scenes.indexOf(ide.scene) + 1; + if (idx > scenes.length()) { + idx = 1; + } + break; + case 'previous': + idx = scenes.indexOf(ide.scene) - 1; + if (idx < 1) { + idx = scenes.length(); + } + break; + case 'last': + idx = scenes.length(); + break; + case 'random': + idx = this.reportBasicRandom(1, scenes.length()); + break; + } + ide.switchToScene(scenes.at(idx)); + return; + } + + scene = detect(scenes.itemsArray(), scn => scn.name === id); + if (scene === null) { + num = parseFloat(id); + if (isNaN(num)) { + return; + } + scene = scenes.at(num); + } + + ide.switchToScene(scene); +}; + + // Process color primitives Process.prototype.setHSVA = function (name, num) { From 9de856e7e3b55c3d1f8098067ab7519983df3998 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 12 Apr 2021 13:13:39 +0200 Subject: [PATCH 20/92] tweaked scene index for removal and ordering --- src/gui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui.js b/src/gui.js index 6c663f3b..1b8883c4 100644 --- a/src/gui.js +++ b/src/gui.js @@ -10247,7 +10247,7 @@ SceneIconMorph.prototype.renameScene = function () { SceneIconMorph.prototype.removeScene = function () { var album = this.parentThatIsA(SceneAlbumMorph), - idx = this.parent.children.indexOf(this), + idx = this.parent.children.indexOf(this) + 1, off = 0, // 2, ide = this.parentThatIsA(IDE_Morph); album.removeSceneAt(idx - off); // ignore buttons From e8900241e0ba3e5162e728a31824297dc6ec7188 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 12 Apr 2021 17:34:14 +0200 Subject: [PATCH 21/92] new Morphic bulk file-drop events --- src/morphic.js | 116 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 28 deletions(-) diff --git a/src/morphic.js b/src/morphic.js index abb40b1e..c189eee5 100644 --- a/src/morphic.js +++ b/src/morphic.js @@ -678,6 +678,15 @@ droppedBinary(anArrayBuffer, name) + In case multiple files are dropped simulateneously the events + + beginBulkDrop() + endBulkDrop() + + are dispatched to to Morphs interested in bracketing the bulk operation, + and the endBulkDrop() event is only signalled after the contents last file + has been asynchronously made available. + (e) keyboard events ------------------- @@ -1280,7 +1289,7 @@ /*global window, HTMLCanvasElement, FileReader, Audio, FileList, Map*/ -var morphicVersion = '2021-February-10'; +var morphicVersion = '2021-Aril-12'; var modules = {}; // keep track of additional loaded modules var useBlurredShadows = true; @@ -11583,6 +11592,9 @@ HandMorph.prototype.processMouseScroll = function (event) { droppedSVG droppedAudio droppedText + + beginBulkDrop + endBulkDrop */ HandMorph.prototype.processDrop = function (event) { @@ -11596,11 +11608,20 @@ HandMorph.prototype.processDrop = function (event) { droppedAudio(audio, name) droppedText(text, name, type) - events to interested Morphs at the mouse pointer + events to interested Morphs at the mouse pointer. + + In case multiple files are dropped simulateneously also displatch + the events + + beginBulkDrop() + endBulkDrop() + + to Morphs interested in bracketing the bulk operation */ var files = event instanceof FileList ? event : event.target.files || event.dataTransfer.files, file, + fileCount, url = event.dataTransfer ? event.dataTransfer.getData('URL') : null, txt = event.dataTransfer ? @@ -11614,11 +11635,15 @@ HandMorph.prototype.processDrop = function (event) { function readSVG(aFile) { var pic = new Image(), - frd = new FileReader(); - while (!target.droppedSVG) { - target = target.parent; + frd = new FileReader(), + trg = target; + while (!trg.droppedSVG) { + trg = trg.parent; } - pic.onload = () => target.droppedSVG(pic, aFile.name); + pic.onload = () => { + trg.droppedSVG(pic, aFile.name); + bulkDrop(); + }; frd = new FileReader(); frd.onloadend = (e) => pic.src = e.target.result; frd.readAsDataURL(aFile); @@ -11626,14 +11651,16 @@ HandMorph.prototype.processDrop = function (event) { function readImage(aFile) { var pic = new Image(), - frd = new FileReader(); - while (!target.droppedImage) { - target = target.parent; + frd = new FileReader(), + trg = target; + while (!trg.droppedImage) { + trg = trg.parent; } pic.onload = () => { canvas = newCanvas(new Point(pic.width, pic.height), true); canvas.getContext('2d').drawImage(pic, 0, 0); - target.droppedImage(canvas, aFile.name); + trg.droppedImage(canvas, aFile.name); + bulkDrop(); }; frd = new FileReader(); frd.onloadend = (e) => pic.src = e.target.result; @@ -11642,39 +11669,64 @@ HandMorph.prototype.processDrop = function (event) { function readAudio(aFile) { var snd = new Audio(), - frd = new FileReader(); - while (!target.droppedAudio) { - target = target.parent; + frd = new FileReader(), + trg = target; + while (!trg.droppedAudio) { + trg = trg.parent; } frd.onloadend = (e) => { snd.src = e.target.result; - target.droppedAudio(snd, aFile.name); + trg.droppedAudio(snd, aFile.name); + bulkDrop(); }; frd.readAsDataURL(aFile); } function readText(aFile) { - var frd = new FileReader(); - while (!target.droppedText) { - target = target.parent; + var frd = new FileReader(), + trg = target; + while (!trg.droppedText) { + trg = trg.parent; } frd.onloadend = (e) => { - target.droppedText(e.target.result, aFile.name, aFile.type); + trg.droppedText(e.target.result, aFile.name, aFile.type); + bulkDrop(); }; frd.readAsText(aFile); } function readBinary(aFile) { - var frd = new FileReader(); - while (!target.droppedBinary) { - target = target.parent; + var frd = new FileReader(), + trg = target; + while (!trg.droppedBinary) { + trg = trg.parent; } frd.onloadend = (e) => { - target.droppedBinary(e.target.result, aFile.name); + trg.droppedBinary(e.target.result, aFile.name); + bulkDrop(); }; frd.readAsArrayBuffer(aFile); } + function beginBulkDrop() { + var trg = target; + while (!trg.beginBulkDrop) { + trg = trg.parent; + } + trg.beginBulkDrop(); + } + + function bulkDrop() { + var trg = target; + fileCount -= 1; + if (files.length > 1 && fileCount === 0) { + while (!trg.endBulkDrop) { + trg = trg.parent; + } + trg.endBulkDrop(); + } + } + function readURL(url, callback) { var request = new XMLHttpRequest(); request.open('GET', url); @@ -11708,6 +11760,10 @@ HandMorph.prototype.processDrop = function (event) { } if (files.length > 0) { + fileCount = files.length; + if (fileCount > 1) { + beginBulkDrop(); + } for (i = 0; i < files.length; i += 1) { file = files[i]; suffix = file.name.slice( @@ -12250,13 +12306,17 @@ WorldMorph.prototype.wantsDropOf = function () { return this.acceptsDrops; }; -WorldMorph.prototype.droppedImage = function () { - return null; -}; +WorldMorph.prototype.droppedImage = nop; -WorldMorph.prototype.droppedSVG = function () { - return null; -}; +WorldMorph.prototype.droppedSVG = nop; + +WorldMorph.prototype.droppedAudio = nop; + +WorldMorph.prototype.droppedText; + +WorldMorph.prototype.beginBulkDrop = nop; + +WorldMorph.prototype.endBulkDrop = nop; // WorldMorph text field tabbing: From 9a8d0cb6ed1b2ffa0552a5e0ee925ddd5f829ba3 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 12 Apr 2021 18:10:53 +0200 Subject: [PATCH 22/92] support bulk-file-drop for importing scenes --- HISTORY.md | 1 + snap.html | 2 +- src/gui.js | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 660eaf0c..7d135e8f 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,7 @@ ### 2021-04-12 * blocks, objects, threads, gui: new "switch to scene _" command primitive +* morphic, gui: support bulk-file-drop for importing scenes ### 2021-04-08 * gui: scroll selected scene icon into view diff --git a/snap.html b/snap.html index a64ff8ad..c9011cbb 100755 --- a/snap.html +++ b/snap.html @@ -5,7 +5,7 @@ Snap! 7 - dev - Build Your Own Blocks - + diff --git a/src/gui.js b/src/gui.js index 1b8883c4..12a7a3e7 100644 --- a/src/gui.js +++ b/src/gui.js @@ -293,6 +293,9 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.savingPreferences = true; // for bh's infamous "Eisenbergification" + this.bulkDropInProgress = false; // for handling multiple file-drops + this.cachedSceneFlag = null; // for importing multiple scenes at once + // initialize inherited properties: IDE_Morph.uber.init.call(this); @@ -2196,6 +2199,17 @@ IDE_Morph.prototype.reactToWorldResize = function (rect) { } }; +IDE_Morph.prototype.beginBulkDrop = function () { + this.bulkDropInProgress = true; + this.cachedSceneFlag = this.isAddingScenes; + this.isAddingScenes = true; +}; + +IDE_Morph.prototype.endBulkDrop = function () { + this.isAddingScenes = this.cachedSceneFlag; + this.bulkDropInProgress = false; +}; + IDE_Morph.prototype.droppedImage = function (aCanvas, name) { var costume = new Costume( aCanvas, @@ -5111,6 +5125,11 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { IDE_Morph.prototype.openProjectString = function (str, callback) { var msg; + if (this.bulkDropInProgress) { + this.rawOpenProjectString(str); + if (callback) {callback(); } + return; + } this.nextSteps([ () => msg = this.showMessage('Opening project...'), () => { From 9caafba65e26893e77c635b233eb341625d3253b Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 12 Apr 2021 19:04:38 +0200 Subject: [PATCH 23/92] tweaked scene album colors --- HISTORY.md | 1 + src/gui.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7d135e8f..7f4f1b51 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,6 +9,7 @@ ### 2021-04-12 * blocks, objects, threads, gui: new "switch to scene _" command primitive * morphic, gui: support bulk-file-drop for importing scenes +* gui: tweaked scene album colors ### 2021-04-08 * gui: scroll selected scene icon into view diff --git a/src/gui.js b/src/gui.js index 12a7a3e7..f5089eed 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1926,7 +1926,7 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { // scenes +++ this.corral.album = keepSceneAlbum ? album : new SceneAlbumMorph(this, this.sliderColor); - this.corral.album.color = this.groupColor; // +++ this.frameColor; + this.corral.album.color = this.frameColor; // this.groupColor; this.corral.add(this.corral.album); this.corral.fixLayout = function () { @@ -10115,9 +10115,9 @@ SceneIconMorph.prototype.init = function (aScene) { var colors, action, query; colors = [ - IDE_Morph.prototype.groupColor, IDE_Morph.prototype.frameColor, - IDE_Morph.prototype.frameColor + IDE_Morph.prototype.groupColor, + IDE_Morph.prototype.groupColor ]; action = () => { From 8ce858d1111fcc672fc7f29a6c54a4d75ad8e38b Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 14 Apr 2021 15:27:32 +0200 Subject: [PATCH 24/92] new Project class --- HISTORY.md | 3 +++ snap.html | 2 +- src/scenes.js | 22 ++++++++++++++++++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7f4f1b51..d54e90ee 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-14 +* scenes: new Project class + ### 2021-04-12 * blocks, objects, threads, gui: new "switch to scene _" command primitive * morphic, gui: support bulk-file-drop for importing scenes diff --git a/snap.html b/snap.html index c9011cbb..0f55c842 100755 --- a/snap.html +++ b/snap.html @@ -11,7 +11,7 @@ - + diff --git a/src/scenes.js b/src/scenes.js index 1dd96419..f8d12084 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -34,6 +34,7 @@ the following list shows the order in which all constructors are defined. Use this list to locate code in this document: + Project Scene credits @@ -47,9 +48,26 @@ // Global stuff //////////////////////////////////////////////////////// -/*global modules, VariableFrame, StageMorph, SpriteMorph, Process*/ +/*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List*/ -modules.scenes = '2021-March-30'; +modules.scenes = '2021-April-14'; + + +// Projecct ///////////////////////////////////////////////////////// + +// I am a container for a set of one or more Snap! scenes, +// the IDE operates on an instance of me + +// Project instance creation: + +function Project() { + this.name = ''; + this.notes = ''; + this.scenes = new List(); + + // for undeleting scenes - do not persist + this.trash = []; +} // Scene ///////////////////////////////////////////////////////// From bc6298940e91b3eec8b838f1fc9a728bf16ebe5a Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 14 Apr 2021 17:42:37 +0200 Subject: [PATCH 25/92] sceneified project deserialization --- HISTORY.md | 1 + snap.html | 2 +- src/store.js | 134 +++++++++++++++++++++++++-------------------------- 3 files changed, 69 insertions(+), 68 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index d54e90ee..673693fa 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,7 @@ ### 2021-04-14 * scenes: new Project class +* store: sceneified projects ### 2021-04-12 * blocks, objects, threads, gui: new "switch to scene _" command primitive diff --git a/snap.html b/snap.html index 0f55c842..71fa257e 100755 --- a/snap.html +++ b/snap.html @@ -21,7 +21,7 @@ - + diff --git a/src/store.js b/src/store.js index feb53b38..1e96d1cf 100644 --- a/src/store.js +++ b/src/store.js @@ -60,7 +60,7 @@ SyntaxElementMorph, BooleanSlotMorph, normalizeCanvas, contains, Scene*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-March-19'; +modules.store = '2021-April-14'; // XML_Serializer /////////////////////////////////////////////////////// @@ -330,13 +330,13 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide, remixID) { SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { // private - var project = new Scene(), + var scene = new Scene(), model, nameID; - this.scene = project; + this.scene = scene; - model = {project: xmlNode }; + model = {scene: xmlNode }; if (+xmlNode.attributes.version > this.version) { throw 'Project uses newer version of Serializer'; } @@ -344,8 +344,8 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { /* Project Info */ this.objects = {}; - project.name = model.project.attributes.name; - if (!project.name) { + scene.name = model.scene.attributes.name; + if (!scene.name) { nameID = 1; while ( Object.prototype.hasOwnProperty.call( @@ -355,126 +355,126 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { ) { nameID += 1; } - project.name = 'Untitled ' + nameID; + scene.name = 'Untitled ' + nameID; } - model.notes = model.project.childNamed('notes'); + model.notes = model.scene.childNamed('notes'); if (model.notes) { - project.notes = model.notes.contents; + scene.notes = model.notes.contents; } - model.globalVariables = model.project.childNamed('variables'); + model.globalVariables = model.scene.childNamed('variables'); /* Stage */ - model.stage = model.project.require('stage'); + model.stage = model.scene.require('stage'); StageMorph.prototype.frameRate = 0; - project.stage.remixID = remixID; + scene.stage.remixID = remixID; if (Object.prototype.hasOwnProperty.call( model.stage.attributes, 'id' )) { - this.objects[model.stage.attributes.id] = project.stage; + this.objects[model.stage.attributes.id] = scene.stage; } if (model.stage.attributes.name) { - project.stage.name = model.stage.attributes.name; + scene.stage.name = model.stage.attributes.name; } if (model.stage.attributes.color) { - project.stage.color = this.loadColor(model.stage.attributes.color); - project.stage.cachedHSV = project.stage.color.hsv(); + scene.stage.color = this.loadColor(model.stage.attributes.color); + scene.stage.cachedHSV = scene.stage.color.hsv(); } if (model.stage.attributes.scheduled === 'true') { - project.stage.fps = 30; + scene.stage.fps = 30; StageMorph.prototype.frameRate = 30; } if (model.stage.attributes.volume) { - project.stage.volume = +model.stage.attributes.volume; + scene.stage.volume = +model.stage.attributes.volume; } if (model.stage.attributes.pan) { - project.stage.pan = +model.stage.attributes.pan; + scene.stage.pan = +model.stage.attributes.pan; } if (model.stage.attributes.penlog) { - project.enablePenLogging = + scene.enablePenLogging = (model.stage.attributes.penlog === 'true'); } model.pentrails = model.stage.childNamed('pentrails'); if (model.pentrails) { - project.pentrails = new Image(); - project.pentrails.onload = function () { - if (project.stage.trailsCanvas) { // work-around a bug in FF - normalizeCanvas(project.stage.trailsCanvas); - var context = project.stage.trailsCanvas.getContext('2d'); - context.drawImage(project.pentrails, 0, 0); - project.stage.changed(); + scene.pentrails = new Image(); + scene.pentrails.onload = function () { + if (scene.stage.trailsCanvas) { // work-around a bug in FF + normalizeCanvas(scene.stage.trailsCanvas); + var context = scene.stage.trailsCanvas.getContext('2d'); + context.drawImage(scene.pentrails, 0, 0); + scene.stage.changed(); } }; - project.pentrails.src = model.pentrails.contents; + scene.pentrails.src = model.pentrails.contents; } - project.stage.setTempo(model.stage.attributes.tempo); + scene.stage.setTempo(model.stage.attributes.tempo); if (model.stage.attributes.width) { - project.stage.dimensions.x = + scene.stage.dimensions.x = Math.max(+model.stage.attributes.width, 240); } if (model.stage.attributes.height) { - project.stage.dimensions.y = + scene.stage.dimensions.y = Math.max(+model.stage.attributes.height, 180); } - project.stage.setExtent(project.stage.dimensions); - project.useFlatLineEnds = + scene.stage.setExtent(scene.stage.dimensions); + scene.useFlatLineEnds = model.stage.attributes.lines === 'flat'; BooleanSlotMorph.prototype.isTernary = model.stage.attributes.ternary !== 'false'; - project.enableHyperOps = + scene.enableHyperOps = model.stage.attributes.hyperops !== 'false'; - project.stage.isThreadSafe = + scene.stage.isThreadSafe = model.stage.attributes.threadsafe === 'true'; - project.enableCodeMapping = + scene.enableCodeMapping = model.stage.attributes.codify === 'true'; - project.enableInheritance = + scene.enableInheritance = model.stage.attributes.inheritance !== 'false'; - project.enableSublistIDs = + scene.enableSublistIDs = model.stage.attributes.sublistIDs === 'true'; - model.hiddenPrimitives = model.project.childNamed('hidden'); + model.hiddenPrimitives = model.scene.childNamed('hidden'); if (model.hiddenPrimitives) { model.hiddenPrimitives.contents.split(' ').forEach( sel => { if (sel) { - project.hiddenPrimitives[sel] = true; + scene.hiddenPrimitives[sel] = true; } } ); } - model.codeHeaders = model.project.childNamed('headers'); + model.codeHeaders = model.scene.childNamed('headers'); if (model.codeHeaders) { model.codeHeaders.children.forEach( - xml => project.codeHeaders[xml.tag] = xml.contents + xml => scene.codeHeaders[xml.tag] = xml.contents ); } - model.codeMappings = model.project.childNamed('code'); + model.codeMappings = model.scene.childNamed('code'); if (model.codeMappings) { model.codeMappings.children.forEach( - xml => project.codeMappings[xml.tag] = xml.contents + xml => scene.codeMappings[xml.tag] = xml.contents ); } - model.globalBlocks = model.project.childNamed('blocks'); + model.globalBlocks = model.scene.childNamed('blocks'); if (model.globalBlocks) { - this.loadCustomBlocks(project.stage, model.globalBlocks, true); + this.loadCustomBlocks(scene.stage, model.globalBlocks, true); this.populateCustomBlocks( - project.stage, + scene.stage, model.globalBlocks, true ); } - this.loadObject(project.stage, model.stage); + this.loadObject(scene.stage, model.stage); /* Sprites */ model.sprites = model.stage.require('sprites'); - project.sprites[project.stage.name] = project.stage; + scene.sprites[scene.stage.name] = scene.stage; model.sprites.childrenNamed('sprite').forEach( model => this.loadValue(model) @@ -538,7 +538,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { if (model.globalVariables) { this.loadVariables( - project.globalVariables, + scene.globalVariables, model.globalVariables ); } @@ -554,7 +554,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { target = Object.prototype.hasOwnProperty.call( model.attributes, 'scope' - ) ? project.sprites[model.attributes.scope] : null; + ) ? scene.sprites[model.attributes.scope] : null; // determine whether the watcher is hidden, slightly // complicated to retain backward compatibility @@ -572,7 +572,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { watcher = new WatcherMorph( model.attributes['var'], color, - isNil(target) ? project.globalVariables + isNil(target) ? scene.globalVariables : target.variables, model.attributes['var'], hidden @@ -592,12 +592,12 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { watcher.setSliderMax(model.attributes.max || '100', true); } watcher.setPosition( - project.stage.topLeft().add(new Point( + scene.stage.topLeft().add(new Point( +model.attributes.x || 0, +model.attributes.y || 0 )) ); - project.stage.add(watcher); + scene.stage.add(watcher); watcher.onNextStep = function () {this.currentValue = null; }; // set watcher's contentsMorph's extent if it is showing a list and @@ -623,7 +623,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { ); this.objects = {}; - return project; + return scene; }; SnapSerializer.prototype.loadBlocks = function (xmlString, targetStage) { @@ -654,26 +654,26 @@ SnapSerializer.prototype.loadBlocks = function (xmlString, targetStage) { SnapSerializer.prototype.loadSprites = function (xmlString, ide) { // public - import a set of sprites represented by xmlString - // into the current project of the ide - var model, project; + // into the current scene of the ide + var model, scene; this.scene = new Scene(ide.stage); - project = this.scene; - project.sprites[project.stage.name] = project.stage; + scene = this.scene; + scene.sprites[scene.stage.name] = scene.stage; model = this.parse(xmlString); if (+model.attributes.version > this.version) { throw 'Module uses newer version of Serializer'; } model.childrenNamed('sprite').forEach(model => { - var sprite = new SpriteMorph(project.globalVariables); + var sprite = new SpriteMorph(scene.globalVariables); if (model.attributes.id) { this.objects[model.attributes.id] = sprite; } if (model.attributes.name) { sprite.name = ide.newSpriteName(model.attributes.name); - project.sprites[sprite.name] = sprite; + scene.sprites[sprite.name] = sprite; } if (model.attributes.color) { sprite.color = this.loadColor(model.attributes.color); @@ -688,7 +688,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { if (model.attributes.pan) { sprite.pan = +model.attributes.pan; } - project.stage.add(sprite); + scene.stage.add(sprite); ide.sprites.add(sprite); sprite.scale = parseFloat(model.attributes.scale || '1'); sprite.rotationStyle = parseFloat( @@ -704,10 +704,10 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { }); // restore inheritance and nesting associations - project.stage.children.forEach(sprite => { + scene.stage.children.forEach(sprite => { var exemplar, anchor; if (sprite.inheritanceInfo) { // only sprites can inherit - exemplar = project.sprites[ + exemplar = scene.sprites[ sprite.inheritanceInfo.exemplar ]; if (exemplar) { @@ -715,14 +715,14 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { } } if (sprite.nestingInfo) { // only sprites may have nesting info - anchor = project.sprites[sprite.nestingInfo.anchor]; + anchor = scene.sprites[sprite.nestingInfo.anchor]; if (anchor) { anchor.attachPart(sprite); } sprite.rotatesWithAnchor = (sprite.nestingInfo.synch === 'true'); } }); - project.stage.children.forEach(sprite => { + scene.stage.children.forEach(sprite => { delete sprite.inheritanceInfo; if (sprite.nestingInfo) { // only sprites may have nesting info sprite.nestingScale = +(sprite.nestingInfo.scale || sprite.scale); @@ -1102,7 +1102,7 @@ SnapSerializer.prototype.loadScript = function (model, object) { var topBlock, block, nextBlock; // Check whether we're importing a single script, not a script as part of a - // whole project + // whole scene if (!this.scene.stage) { this.scene.stage = object.parentThatIsA(StageMorph); this.scene.targetStage = this.scene.stage; From 298f559775a66e25b57f1d7c75363284aba3a3ed Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 14 Apr 2021 18:40:42 +0200 Subject: [PATCH 26/92] switched to scene-based project serialization --- HISTORY.md | 1 + snap.html | 2 +- src/gui.js | 32 ++++++++++++++++---------------- src/store.js | 12 ++++++++++++ 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 673693fa..6b30ee80 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,6 +9,7 @@ ### 2021-04-14 * scenes: new Project class * store: sceneified projects +* gui: switched to scene-based project serialization ### 2021-04-12 * blocks, objects, threads, gui: new "switch to scene _" command primitive diff --git a/snap.html b/snap.html index 71fa257e..a069e053 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index f5089eed..7ca797f3 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-12'; +modules.gui = '2021-April-14'; // Declarations @@ -2586,12 +2586,12 @@ IDE_Morph.prototype.refreshIDE = function () { if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } SpriteMorph.prototype.initBlocks(); this.buildPanes(); @@ -2784,7 +2784,7 @@ IDE_Morph.prototype.backupAndDo = function (callback) { // private var username = this.cloud.username; try { - localStorage['-snap-backup-'] = this.serializer.serialize(this.stage); + localStorage['-snap-backup-'] = this.serializer.serialize(this.scene); delete localStorage['-snap-bakflag-']; if (username) { localStorage['-snap-bakuser-'] = username; @@ -4714,7 +4714,7 @@ IDE_Morph.prototype.exportProject = function (name, plain) { dataPrefix = 'data:text/' + plain ? 'plain,' : 'xml,'; try { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.stage); + str = this.serializer.serialize(this.scene); this.setURL('#open:' + dataPrefix + encodeURIComponent(str)); this.saveXMLAs(str, name); menu.destroy(); @@ -5658,12 +5658,12 @@ IDE_Morph.prototype.toggleDynamicInputLabels = function () { !SyntaxElementMorph.prototype.dynamicInputLabels; if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } SpriteMorph.prototype.initBlocks(); this.spriteBar.tabBar.tabTo('scripts'); @@ -6025,12 +6025,12 @@ IDE_Morph.prototype.reflectLanguage = function (lang, callback, noSave) { if (!this.loadNewProject) { if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } } SpriteMorph.prototype.initBlocks(); @@ -6133,12 +6133,12 @@ IDE_Morph.prototype.setBlocksScale = function (num) { var projectData; if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(this.stage); + projectData = this.serializer.serialize(this.scene); } SyntaxElementMorph.prototype.setScale(num); CommentMorph.prototype.refreshScale(); @@ -6473,7 +6473,7 @@ IDE_Morph.prototype.logout = function () { }; IDE_Morph.prototype.buildProjectRequest = function () { - var xml = this.serializer.serialize(this.stage), + var xml = this.serializer.serialize(this.scene), thumbnail = normalizeCanvas( this.stage.thumbnail( SnapSerializer.prototype.thumbnailSize @@ -6589,7 +6589,7 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { if (Process.prototype.isCatchingErrors) { try { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.stage); + str = this.serializer.serialize(this.scene); this.saveXMLAs(str, this.projectName); menu.destroy(); this.showMessage('Exported!', 1); @@ -6599,7 +6599,7 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { } } else { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.stage); + str = this.serializer.serialize(this.scene); this.saveXMLAs(str, this.projectName); menu.destroy(); this.showMessage('Exported!', 1); @@ -6617,7 +6617,7 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { if (Process.prototype.isCatchingErrors) { try { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.stage); + str = this.serializer.serialize(this.scene); media = this.serializer.mediaXML(name); dta = '' + str + media + ''; this.saveXMLAs(str, this.projectName); @@ -6629,7 +6629,7 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { } } else { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.stage); + str = this.serializer.serialize(this.scene); media = this.serializer.mediaXML(name); dta = '' + str + media + ''; this.saveXMLAs(str, this.projectName); diff --git a/src/store.js b/src/store.js index 1e96d1cf..ec800271 100644 --- a/src/store.js +++ b/src/store.js @@ -1619,6 +1619,18 @@ Array.prototype.toXML = function (serializer) { ); }; +// Scenes + +Scene.prototype.toXML = function (serializer) { + var tmp = new Scene(), + xml; + tmp.captureGlobalSettings(); + this.applyGlobalSettings(); + xml = this.stage.toXML(serializer); + tmp.applyGlobalSettings(); + return xml; +}; + // Sprites StageMorph.prototype.toXML = function (serializer) { From 5816f1fbfee4e88af04eeb95e556fe7903758c2c Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 16 Apr 2021 08:09:04 +0200 Subject: [PATCH 27/92] remember the sprite last edited when saving a project --- HISTORY.md | 5 +++++ snap.html | 6 +++--- src/gui.js | 14 +++++--------- src/scenes.js | 30 ++++++++++++++++++++++++++++-- src/store.js | 27 ++++++++++++++++----------- 5 files changed, 57 insertions(+), 25 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6b30ee80..ac559234 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,9 +3,14 @@ ## in development for v7: * **New Features:** * Scenes +* **Notable Changes:** + * saved projects remember the last edited srpite * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-16 +* scenes, store, gui: remember last edited sprite in a scene / project + ### 2021-04-14 * scenes: new Project class * store: sceneified projects diff --git a/snap.html b/snap.html index a069e053..1d08c275 100755 --- a/snap.html +++ b/snap.html @@ -11,8 +11,8 @@ - - + + @@ -21,7 +21,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 7ca797f3..780fbe3b 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-14'; +modules.gui = '2021-April-16'; // Declarations @@ -246,7 +246,7 @@ IDE_Morph.prototype.init = function (isAutoFill) { // editor this.globalVariables = this.scene.globalVariables; this.currentSprite = this.scene.addDefaultSprite(); - this.sprites = new List([this.currentSprite]); + this.sprites = this.scene.sprites; this.projectName = this.scene.name; this.projectNotes = this.scene.notes; this.currentCategory = 'motion'; @@ -2543,6 +2543,7 @@ IDE_Morph.prototype.selectSprite = function (sprite) { this.currentSprite.scripts.focus.stopEditing(); } this.currentSprite = sprite; + this.scene.currentSprite = sprite; this.createPalette(); this.createSpriteBar(); this.createSpriteEditor(); @@ -5398,7 +5399,6 @@ IDE_Morph.prototype.openScene = function (scene) { }; IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { - var sprites = []; if (!scene || !scene.stage) { return; } @@ -5413,14 +5413,10 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { this.stage.destroy(); this.add(scene.stage); this.stage = scene.stage; - sprites = this.stage.children.filter( - child => child instanceof SpriteMorph - ); - sprites.sort((x, y) => x.idx - y.idx); - this.sprites = new List(sprites); + this.sprites = scene.sprites; this.stage.pauseGenericHatBlocks(); this.createCorral(!refreshAlbum); // keep scenes - this.selectSprite(sprites[0] || this.stage); + this.selectSprite(this.scene.currentSprite); this.fixLayout(); this.corral.album.updateSelection(); this.corral.album.contents.children.forEach(function (morph) { diff --git a/src/scenes.js b/src/scenes.js index f8d12084..d6788c3c 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -50,7 +50,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List*/ -modules.scenes = '2021-April-14'; +modules.scenes = '2021-April-16'; // Projecct ///////////////////////////////////////////////////////// @@ -85,6 +85,10 @@ function Scene(aStageMorph) { aStageMorph.globalVariables() : new VariableFrame(); this.stage = aStageMorph || new StageMorph(this.globalVariables); + // cached IDE state + this.sprites = new List(); + this.currentSprite = null; + // global settings (shared) this.hiddenPrimitives = {}; this.codeMappings = {}; @@ -100,13 +104,33 @@ function Scene(aStageMorph) { this.enableHyperOps = true; // for deserializing - do not persist - this.sprites = {}; + this.spritesDict = {}; this.targetStage = null; + this.spriteIdx = null; // for undeleting sprites - do not persist this.trash = []; } +Scene.prototype.initialize = function () { + // initialize after deserializing + // only to be called by store + var objs = this.stage.children.filter( + child => child instanceof SpriteMorph + ); + objs.sort((x, y) => x.idx - y.idx); + this.sprites = new List(objs); + if (this.spriteIdx === null && this.sprites.length() > 0) { + this.currentSprite = this.sprites.at(1); + } else if (this.spriteIdx === 0) { + this.currentSprite = this.stage; + } else { + this.currentSprite = this.sprites.at(this.spriteIdx) || + this.stage; + } + return this; +}; + Scene.prototype.addDefaultSprite = function () { var sprite = new SpriteMorph(this.globalVariables); sprite.setPosition( @@ -115,6 +139,8 @@ Scene.prototype.addDefaultSprite = function () { ) ); this.stage.add(sprite); + this.sprites.add(sprite); + this.currentSprite = sprite; return sprite; }; diff --git a/src/store.js b/src/store.js index ec800271..eae17636 100644 --- a/src/store.js +++ b/src/store.js @@ -60,7 +60,7 @@ SyntaxElementMorph, BooleanSlotMorph, normalizeCanvas, contains, Scene*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-April-14'; +modules.store = '2021-April-16'; // XML_Serializer /////////////////////////////////////////////////////// @@ -392,6 +392,9 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { if (model.stage.attributes.pan) { scene.stage.pan = +model.stage.attributes.pan; } + if (model.stage.attributes.select) { + scene.spriteIdx = +model.stage.attributes.select; + } if (model.stage.attributes.penlog) { scene.enablePenLogging = (model.stage.attributes.penlog === 'true'); @@ -474,7 +477,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { /* Sprites */ model.sprites = model.stage.require('sprites'); - scene.sprites[scene.stage.name] = scene.stage; + scene.spritesDict[scene.stage.name] = scene.stage; model.sprites.childrenNamed('sprite').forEach( model => this.loadValue(model) @@ -484,7 +487,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { this.scene.stage.children.forEach(sprite => { var exemplar, anchor; if (sprite.inheritanceInfo) { // only sprites can inherit - exemplar = this.scene.sprites[ + exemplar = this.scene.spritesDict[ sprite.inheritanceInfo.exemplar ]; if (exemplar) { @@ -494,7 +497,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { sprite.updatePropagationCache(); } if (sprite.nestingInfo) { // only sprites may have nesting info - anchor = this.scene.sprites[sprite.nestingInfo.anchor]; + anchor = this.scene.spritesDict[sprite.nestingInfo.anchor]; if (anchor) { anchor.attachPart(sprite); } @@ -554,7 +557,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { target = Object.prototype.hasOwnProperty.call( model.attributes, 'scope' - ) ? scene.sprites[model.attributes.scope] : null; + ) ? scene.spritesDict[model.attributes.scope] : null; // determine whether the watcher is hidden, slightly // complicated to retain backward compatibility @@ -623,7 +626,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { ); this.objects = {}; - return scene; + return scene.initialize(); }; SnapSerializer.prototype.loadBlocks = function (xmlString, targetStage) { @@ -659,7 +662,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { this.scene = new Scene(ide.stage); scene = this.scene; - scene.sprites[scene.stage.name] = scene.stage; + scene.spritesDict[scene.stage.name] = scene.stage; model = this.parse(xmlString); if (+model.attributes.version > this.version) { @@ -673,7 +676,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { } if (model.attributes.name) { sprite.name = ide.newSpriteName(model.attributes.name); - scene.sprites[sprite.name] = sprite; + scene.spritesDict[sprite.name] = sprite; } if (model.attributes.color) { sprite.color = this.loadColor(model.attributes.color); @@ -707,7 +710,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { scene.stage.children.forEach(sprite => { var exemplar, anchor; if (sprite.inheritanceInfo) { // only sprites can inherit - exemplar = scene.sprites[ + exemplar = scene.spritesDict[ sprite.inheritanceInfo.exemplar ]; if (exemplar) { @@ -715,7 +718,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) { } } if (sprite.nestingInfo) { // only sprites may have nesting info - anchor = scene.sprites[sprite.nestingInfo.anchor]; + anchor = scene.spritesDict[sprite.nestingInfo.anchor]; if (anchor) { anchor.attachPart(sprite); } @@ -1408,7 +1411,7 @@ SnapSerializer.prototype.loadValue = function (model, object) { } if (model.attributes.name) { v.name = model.attributes.name; - this.scene.sprites[model.attributes.name] = v; + this.scene.spritesDict[model.attributes.name] = v; } if (model.attributes.idx) { v.idx = +model.attributes.idx; @@ -1672,6 +1675,7 @@ StageMorph.prototype.toXML = function (serializer) { '$' + ' Date: Fri, 16 Apr 2021 09:23:37 +0200 Subject: [PATCH 28/92] removed Project class --- src/scenes.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/scenes.js b/src/scenes.js index d6788c3c..3c53047b 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -34,7 +34,6 @@ the following list shows the order in which all constructors are defined. Use this list to locate code in this document: - Project Scene credits @@ -52,23 +51,6 @@ modules.scenes = '2021-April-16'; - -// Projecct ///////////////////////////////////////////////////////// - -// I am a container for a set of one or more Snap! scenes, -// the IDE operates on an instance of me - -// Project instance creation: - -function Project() { - this.name = ''; - this.notes = ''; - this.scenes = new List(); - - // for undeleting scenes - do not persist - this.trash = []; -} - // Scene ///////////////////////////////////////////////////////// // I am a container for a Snap! stage, scene-global variables From b65b941a3967f63ff458ced11dbf2e0d02978bf3 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 16 Apr 2021 09:24:22 +0200 Subject: [PATCH 29/92] Update HISTORY.md --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index ac559234..84f7ada4 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ ### 2021-04-16 * scenes, store, gui: remember last edited sprite in a scene / project +* scenes: removed Project class ### 2021-04-14 * scenes: new Project class From a74779f39bcd3928b839a06b2aac62e780613f95 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 16 Apr 2021 12:30:45 +0200 Subject: [PATCH 30/92] export multi-scene projects --- HISTORY.md | 1 + src/gui.js | 35 +++++++++++++++++++++++++++++++++-- src/scenes.js | 18 ++++++++++++++++++ src/store.js | 12 ++++++++++-- 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 84f7ada4..eea9025e 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,7 @@ ### 2021-04-16 * scenes, store, gui: remember last edited sprite in a scene / project * scenes: removed Project class +* scenes, store, gui: export multi-scene projects ### 2021-04-14 * scenes: new Project class diff --git a/src/gui.js b/src/gui.js index 780fbe3b..e0220668 100644 --- a/src/gui.js +++ b/src/gui.js @@ -67,7 +67,7 @@ */ /*global modules, Morph, SpriteMorph, SyntaxElementMorph, Color, Cloud, Audio, -ListWatcherMorph, TextMorph, newCanvas, useBlurredShadows, Sound, Scene, +ListWatcherMorph, TextMorph, newCanvas, useBlurredShadows, Sound, Scene, Note, StringMorph, Point, MenuMorph, morphicVersion, DialogBoxMorph, normalizeCanvas, ToggleButtonMorph, contains, ScrollFrameMorph, StageMorph, PushButtonMorph, sb, InputFieldMorph, FrameMorph, Process, nop, SnapSerializer, ListMorph, detect, @@ -79,7 +79,7 @@ CommandBlockMorph, BooleanSlotMorph, RingReporterSlotMorph, ScriptFocusMorph, BlockLabelPlaceHolderMorph, SpeechBubbleMorph, XML_Element, WatcherMorph, WHITE, BlockRemovalDialogMorph,TableMorph, isSnapObject, isRetinaEnabled, SliderMorph, disableRetinaSupport, enableRetinaSupport, isRetinaSupported, MediaRecorder, -Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ +Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// @@ -4710,6 +4710,11 @@ IDE_Morph.prototype.exportProject = function (name, plain) { // newWindow requests displaying the project in a new tab. var menu, str, dataPrefix; + if (this.scenes.length() > 1) { // +++ + this.exportScenes(this.scenes.at(1).name, plain); + return; + } + if (name) { this.setProjectName(name); dataPrefix = 'data:text/' + plain ? 'plain,' : 'xml,'; @@ -4731,6 +4736,32 @@ IDE_Morph.prototype.exportProject = function (name, plain) { } }; +IDE_Morph.prototype.exportScenes = function (name, plain) { // +++ + // experimental export of multi-scene project - under construction + + var menu, str, dataPrefix; + + if (name) { + this.setProjectName(name); + dataPrefix = 'data:text/' + plain ? 'plain,' : 'xml,'; + try { + menu = this.showMessage('Exporting'); + str = this.serializer.serialize(new Project(this.scenes)); + this.setURL('#open:' + dataPrefix + encodeURIComponent(str)); + this.saveXMLAs(str, name); + menu.destroy(); + this.recordSavedChanges(); + this.showMessage('Exported!', 1); + } catch (err) { + if (Process.prototype.isCatchingErrors) { + this.showMessage('Export failed: ' + err); + } else { + throw err; + } + } + } +}; + IDE_Morph.prototype.exportGlobalBlocks = function () { if (this.stage.globalBlocks.length > 0) { new BlockExportDialogMorph( diff --git a/src/scenes.js b/src/scenes.js index 3c53047b..07c5a3f6 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -34,6 +34,7 @@ the following list shows the order in which all constructors are defined. Use this list to locate code in this document: + Project Scene credits @@ -51,6 +52,23 @@ modules.scenes = '2021-April-16'; + +// Projecct ///////////////////////////////////////////////////////// + +// I am a container for a set of one or more Snap! scenes, +// the IDE operates on an instance of me + +// Project instance creation: + +function Project(scenes) { + this.name = ''; + this.notes = ''; + this.scenes = scenes || new List(); + + // for undeleting scenes - do not persist + this.trash = []; +} + // Scene ///////////////////////////////////////////////////////// // I am a container for a Snap! stage, scene-global variables diff --git a/src/store.js b/src/store.js index eae17636..2a382226 100644 --- a/src/store.js +++ b/src/store.js @@ -56,7 +56,8 @@ Color, List, newCanvas, Costume, Audio, IDE_Morph, ScriptsMorph, ArgLabelMorph, BlockMorph, ArgMorph, InputSlotMorph, TemplateSlotMorph, CommandSlotMorph, FunctionSlotMorph, MultiArgMorph, ColorSlotMorph, nop, CommentMorph, isNil, localize, SVG_Costume, MorphicPreferences, Process, isSnapObject, Variable, -SyntaxElementMorph, BooleanSlotMorph, normalizeCanvas, contains, Scene*/ +SyntaxElementMorph, BooleanSlotMorph, normalizeCanvas, contains, Scene, +Project*/ // Global stuff //////////////////////////////////////////////////////// @@ -1622,7 +1623,14 @@ Array.prototype.toXML = function (serializer) { ); }; -// Scenes +// Scenes & multi-scene projects + +Project.prototype.toXML = function (serializer) { + return serializer.format( + '%', + serializer.store(this.scenes.itemsArray()) + ); +}; Scene.prototype.toXML = function (serializer) { var tmp = new Scene(), From cc56f710bfeaeb39f3220a192c6212122af15463 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 20 Apr 2021 09:09:03 +0200 Subject: [PATCH 31/92] multi-scene project serialization format, first pass --- HISTORY.md | 3 ++ snap.html | 6 +-- src/gui.js | 6 +-- src/scenes.js | 7 +-- src/store.js | 145 ++++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 153 insertions(+), 14 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index eea9025e..a1e43adf 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-20 +* scenes, store, gui: multi-scene project serialization format, first pass + ### 2021-04-16 * scenes, store, gui: remember last edited sprite in a scene / project * scenes: removed Project class diff --git a/snap.html b/snap.html index 1d08c275..dd8be232 100755 --- a/snap.html +++ b/snap.html @@ -11,8 +11,8 @@ - - + + @@ -21,7 +21,7 @@ - + diff --git a/src/gui.js b/src/gui.js index e0220668..50d99209 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-16'; +modules.gui = '2021-April-20'; // Declarations @@ -4710,10 +4710,10 @@ IDE_Morph.prototype.exportProject = function (name, plain) { // newWindow requests displaying the project in a new tab. var menu, str, dataPrefix; - if (this.scenes.length() > 1) { // +++ + // if (this.scenes.length() > 1) { // +++ this.exportScenes(this.scenes.at(1).name, plain); return; - } + // } if (name) { this.setProjectName(name); diff --git a/src/scenes.js b/src/scenes.js index 07c5a3f6..8910b7c9 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -50,7 +50,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List*/ -modules.scenes = '2021-April-16'; +modules.scenes = '2021-April-20'; // Projecct ///////////////////////////////////////////////////////// @@ -61,8 +61,9 @@ modules.scenes = '2021-April-16'; // Project instance creation: function Project(scenes) { - this.name = ''; - this.notes = ''; + this.name = 'Test'; + this.notes = 'some notes'; + this.thumbnail = null; this.scenes = scenes || new List(); // for undeleting scenes - do not persist diff --git a/src/store.js b/src/store.js index 2a382226..68bf2027 100644 --- a/src/store.js +++ b/src/store.js @@ -61,7 +61,7 @@ Project*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-April-16'; +modules.store = '2021-April-20'; // XML_Serializer /////////////////////////////////////////////////////// @@ -87,7 +87,7 @@ function XML_Serializer() { XML_Serializer.prototype.idProperty = 'serializationID'; XML_Serializer.prototype.mediaIdProperty = 'serializationMediaID'; XML_Serializer.prototype.mediaDetectionProperty = 'isMedia'; -XML_Serializer.prototype.version = 1; // increment on structural change +XML_Serializer.prototype.version = 2; // increment on structural change // XML_Serializer accessing: @@ -247,7 +247,7 @@ SnapSerializer.uber = XML_Serializer.prototype; // SnapSerializer constants: -SnapSerializer.prototype.app = 'Snap! 6, https://snap.berkeley.edu'; +SnapSerializer.prototype.app = 'Snap! 7dev, https://snap.berkeley.edu'; SnapSerializer.prototype.thumbnailSize = new Point(160, 120); @@ -1626,24 +1626,91 @@ Array.prototype.toXML = function (serializer) { // Scenes & multi-scene projects Project.prototype.toXML = function (serializer) { + var thumbdata; + + // thumb data catch cross-origin tainting exception when using SVG costumes + try { + thumbdata = this.thumbnail.toDataURL('image/png'); + } catch (error) { + thumbdata = null; + } + return serializer.format( - '%', + '' + + '$' + + '$' + + '%' + + '', + this.name || localize('Untitled'), + serializer.app, + serializer.version, + this.notes || '', + thumbdata, serializer.store(this.scenes.itemsArray()) ); }; Scene.prototype.toXML = function (serializer) { var tmp = new Scene(), + thumbdata, xml; + + function code(key) { + var str = ''; + Object.keys(StageMorph.prototype[key]).forEach( + selector => { + str += ( + '<' + selector + '>' + + XML_Element.prototype.escape( + StageMorph.prototype[key][selector] + ) + + '' + ); + } + ); + return str; + } + + // catch cross-origin tainting exception when using SVG costumes + try { + thumbdata = this.thumbnail.toDataURL('image/png'); + } catch (error) { + thumbdata = null; + } + tmp.captureGlobalSettings(); this.applyGlobalSettings(); - xml = this.stage.toXML(serializer); + xml = serializer.format( + '' + + '$' + + '$' + + '$' + + '%' + + '%' + + '%' + + '%' + + '%' + // stage + '', + this.name || localize('Untitled'), + this.notes || '', + thumbdata, + Object.keys(StageMorph.prototype.hiddenPrimitives).reduce( + (a, b) => a + ' ' + b, + '' + ), + code('codeHeaders'), + code('codeMappings'), + serializer.store(this.stage.globalBlocks), + serializer.store(this.globalVariables), + serializer.store(this.stage) + ); tmp.applyGlobalSettings(); return xml; }; // Sprites +/* StageMorph.prototype.toXML = function (serializer) { var thumbnail = normalizeCanvas( this.thumbnail(SnapSerializer.prototype.thumbnailSize), @@ -1761,6 +1828,74 @@ StageMorph.prototype.toXML = function (serializer) { serializer.store(ide.globalVariables) : '' ); }; +*/ + +StageMorph.prototype.toXML = function (serializer) { + var costumeIdx = this.getCostumeIdx(), + ide = this.parentThatIsA(IDE_Morph); + + this.removeAllClones(); + return serializer.format( + '' + + '$' + + '%' + // current costume, if it's not in the wardrobe + '%' + + '%' + + '%' + + '%' + + '%' + + '%' + + '', + this.dimensions.x, + this.dimensions.y, + costumeIdx, + this.color.r, + this.color.g, + this.color.b, + this.color.a, + this.getTempo(), + this.isThreadSafe, + ide.sprites.asArray().indexOf(ide.currentSprite) + 1, + this.enablePenLogging, + this.instrument ? + ' instrument="' + parseInt(this.instrument) + '" ' : '', + this.volume, + this.pan, + SpriteMorph.prototype.useFlatLineEnds ? 'flat' : 'round', + BooleanSlotMorph.prototype.isTernary, + Process.prototype.enableHyperOps === true, + this.enableCodeMapping, + this.enableInheritance, + this.enableSublistIDs, + StageMorph.prototype.frameRate !== 0, + normalizeCanvas(this.trailsCanvas, true).toDataURL('image/png'), + + // current costume, if it's not in the wardrobe + !costumeIdx && this.costume ? + '' + serializer.store(this.costume) + '' + : '', + + serializer.store(this.costumes, this.name + '_cst'), + serializer.store(this.sounds, this.name + '_snd'), + serializer.store(this.variables), + serializer.store(this.customBlocks), + serializer.store(this.scripts), + serializer.store(this.children) + ); +}; SpriteMorph.prototype.toXML = function (serializer) { var stage = this.parentThatIsA(StageMorph), From 69f7e9990083a1b582ad7dd90bf889ea02fafe60 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 21 Apr 2021 16:59:37 +0200 Subject: [PATCH 32/92] refactored project loading structure --- HISTORY.md | 3 +++ snap.html | 2 +- src/store.js | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a1e43adf..a6a894a1 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-21 +* store: refactored project loading structure + ### 2021-04-20 * scenes, store, gui: multi-scene project serialization format, first pass diff --git a/snap.html b/snap.html index dd8be232..69511343 100755 --- a/snap.html +++ b/snap.html @@ -21,7 +21,7 @@ - + diff --git a/src/store.js b/src/store.js index 68bf2027..8b18d027 100644 --- a/src/store.js +++ b/src/store.js @@ -61,7 +61,7 @@ Project*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-April-20'; +modules.store = '2021-April-21'; // XML_Serializer /////////////////////////////////////////////////////// @@ -326,10 +326,10 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide, remixID) { '\n\nand may be incompatible or fail to load here.' ); } - return this.rawLoadProjectModel(xmlNode, remixID); + return this.loadScene(xmlNode, remixID); }; -SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) { +SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { // private var scene = new Scene(), model, From a558af4552b888a43e708ebef0a811a2d4aff8c8 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 21 Apr 2021 18:21:33 +0200 Subject: [PATCH 33/92] more project loading structure refactorings --- HISTORY.md | 2 +- snap.html | 2 +- src/gui.js | 15 ++++++++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a6a894a1..dc173e34 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,7 +9,7 @@ * made scrollbars in the wardrobe and jukebox more responsive ### 2021-04-21 -* store: refactored project loading structure +* store, gui: refactored project loading structure ### 2021-04-20 * scenes, store, gui: multi-scene project serialization format, first pass diff --git a/snap.html b/snap.html index 69511343..d43fb468 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 50d99209..93700a0d 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-20'; +modules.gui = '2021-April-21'; // Declarations @@ -5410,7 +5410,7 @@ IDE_Morph.prototype.rawOpenDataString = function (str, name, type) { } }; -IDE_Morph.prototype.openProject = function (name) { +IDE_Morph.prototype.openProjectName = function (name) { var str; if (name) { this.showMessage('opening project\n' + name); @@ -5421,6 +5421,15 @@ IDE_Morph.prototype.openProject = function (name) { } }; +IDE_Morph.prototype.openProject = function (project) { + if (this.isAddingScenes) { + project.scenes.itemsArray().forEach(scene => this.scenes.add(scene)); + } else { + this.scenes = project.scenes; + } + this.switchToScene(project.scenes.at(1), true); // refresh album +}; + IDE_Morph.prototype.openScene = function (scene) { if (!this.isAddingScenes) { this.scenes = new List(); @@ -7615,7 +7624,7 @@ ProjectDialogMorph.prototype.openProject = function () { } else { // 'local' this.ide.source = null; - this.ide.backup(() => this.ide.openProject(proj.name)); + this.ide.backup(() => this.ide.openProjectName(proj.name)); this.destroy(); } }; From 5e5802c4695e356cdf3cbf10b92d4839e294fb11 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 22 Apr 2021 18:23:10 +0200 Subject: [PATCH 34/92] first pass at deserializing multi-scene projects --- HISTORY.md | 3 +++ snap.html | 4 ++-- src/gui.js | 4 ++-- src/store.js | 15 ++++++++++++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index dc173e34..8d37c12b 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-22 +* store, gui: first pass at deserializing multi-scene projects + ### 2021-04-21 * store, gui: refactored project loading structure diff --git a/snap.html b/snap.html index d43fb468..559bc8ee 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + @@ -21,7 +21,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 93700a0d..d3d876b5 100644 --- a/src/gui.js +++ b/src/gui.js @@ -5177,14 +5177,14 @@ IDE_Morph.prototype.rawOpenProjectString = function (str) { this.spriteBar.tabBar.tabTo('scripts'); if (Process.prototype.isCatchingErrors) { try { - this.openScene( + this.openProject( this.serializer.load(str, this) ); } catch (err) { this.showMessage('Load failed: ' + err); } } else { - this.openScene( + this.openProject( this.serializer.load(str, this) ); } diff --git a/src/store.js b/src/store.js index 8b18d027..ac2d7872 100644 --- a/src/store.js +++ b/src/store.js @@ -61,7 +61,7 @@ Project*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-April-21'; +modules.store = '2021-April-22'; // XML_Serializer /////////////////////////////////////////////////////// @@ -316,7 +316,9 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide, remixID) { // show a warning if the origin apps differ var appInfo = xmlNode.attributes.app, - app = appInfo ? appInfo.split(' ')[0] : null; + app = appInfo ? appInfo.split(' ')[0] : null, + scenesModel = xmlNode.childNamed('scenes'), + project = new Project(); if (ide && app && app !== this.app.split(' ')[0]) { ide.inform( @@ -326,7 +328,14 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide, remixID) { '\n\nand may be incompatible or fail to load here.' ); } - return this.loadScene(xmlNode, remixID); + if (scenesModel) { + scenesModel.childrenNamed('scene').forEach(model => + project.scenes.add(this.loadScene(model)) + ); + } else { + project.scenes.add(this.loadScene(xmlNode, remixID)); + } + return project; }; SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { From d278a91e7f308a240190e42a7c6de66de787030e Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 22 Apr 2021 18:38:26 +0200 Subject: [PATCH 35/92] migrated "new project" feature --- HISTORY.md | 1 + snap.html | 2 +- src/gui.js | 9 +++------ src/scenes.js | 8 +++++++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 8d37c12b..0a7d0a13 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects +* gui, scenes: migrated "new project" feature ### 2021-04-21 * store, gui: refactored project loading structure diff --git a/snap.html b/snap.html index 559bc8ee..34ffa557 100755 --- a/snap.html +++ b/snap.html @@ -11,7 +11,7 @@ - + diff --git a/src/gui.js b/src/gui.js index d3d876b5..4ad726c6 100644 --- a/src/gui.js +++ b/src/gui.js @@ -4655,17 +4655,14 @@ IDE_Morph.prototype.editProjectNotes = function () { }; IDE_Morph.prototype.newProject = function () { - var scene = new Scene(); + var project = new Project(); - scene.addDefaultSprite(); + project.addDefaultScene(); this.source = this.cloud.username ? 'cloud' : null; - if (this.stage) { - this.stage.destroy(); - } if (location.hash.substr(0, 6) !== '#lang:') { location.hash = ''; } - this.openScene(scene); + this.openProject(project); }; IDE_Morph.prototype.save = function () { diff --git a/src/scenes.js b/src/scenes.js index 8910b7c9..4f98aafa 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -50,7 +50,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List*/ -modules.scenes = '2021-April-20'; +modules.scenes = '2021-April-22'; // Projecct ///////////////////////////////////////////////////////// @@ -70,6 +70,12 @@ function Project(scenes) { this.trash = []; } +Project.prototype.addDefaultScene = function () { + var scene = new Scene(); + scene.addDefaultSprite(); + this.scenes.add(scene); +}; + // Scene ///////////////////////////////////////////////////////// // I am a container for a Snap! stage, scene-global variables From bc14b97a7acbffa5483131e370e98e361b5433d4 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 22 Apr 2021 18:49:21 +0200 Subject: [PATCH 36/92] replaced openScene() with openProject() --- HISTORY.md | 1 + src/gui.js | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 0a7d0a13..c47caf16 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,7 @@ ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects * gui, scenes: migrated "new project" feature +* gui: replaced openScene() with openProject() ### 2021-04-21 * store, gui: refactored project loading structure diff --git a/src/gui.js b/src/gui.js index 4ad726c6..372e4718 100644 --- a/src/gui.js +++ b/src/gui.js @@ -5207,7 +5207,7 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) { try { model = this.serializer.parse(str); this.serializer.loadMediaModel(model.childNamed('media')); - this.openScene( + this.openProject( this.serializer.loadProjectModel( model.childNamed('project'), this, @@ -5220,7 +5220,7 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) { } else { model = this.serializer.parse(str); this.serializer.loadMediaModel(model.childNamed('media')); - this.openScene( + this.openProject( this.serializer.loadProjectModel( model.childNamed('project'), this, @@ -5427,14 +5427,6 @@ IDE_Morph.prototype.openProject = function (project) { this.switchToScene(project.scenes.at(1), true); // refresh album }; -IDE_Morph.prototype.openScene = function (scene) { - if (!this.isAddingScenes) { - this.scenes = new List(); - } - this.scenes.add(scene); - this.switchToScene(scene, this.isAddingScenes); -}; - IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { if (!scene || !scene.stage) { return; From ddfb17aaae01f8fc6c51a9b70ac39dffc755dc27 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 22 Apr 2021 19:15:53 +0200 Subject: [PATCH 37/92] unified exporting projects --- src/gui.js | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/gui.js b/src/gui.js index 372e4718..7be1c625 100644 --- a/src/gui.js +++ b/src/gui.js @@ -4707,36 +4707,7 @@ IDE_Morph.prototype.exportProject = function (name, plain) { // newWindow requests displaying the project in a new tab. var menu, str, dataPrefix; - // if (this.scenes.length() > 1) { // +++ - this.exportScenes(this.scenes.at(1).name, plain); - return; - // } - - if (name) { - this.setProjectName(name); - dataPrefix = 'data:text/' + plain ? 'plain,' : 'xml,'; - try { - menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.scene); - this.setURL('#open:' + dataPrefix + encodeURIComponent(str)); - this.saveXMLAs(str, name); - menu.destroy(); - this.recordSavedChanges(); - this.showMessage('Exported!', 1); - } catch (err) { - if (Process.prototype.isCatchingErrors) { - this.showMessage('Export failed: ' + err); - } else { - throw err; - } - } - } -}; - -IDE_Morph.prototype.exportScenes = function (name, plain) { // +++ - // experimental export of multi-scene project - under construction - - var menu, str, dataPrefix; + name = this.scenes.at(1).name; // +++ if (name) { this.setProjectName(name); From 80743bff62c9c2755eaa39589a86a017fc2e096b Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 22 Apr 2021 19:16:47 +0200 Subject: [PATCH 38/92] removed old stage serialization code --- src/store.js | 120 --------------------------------------------------- 1 file changed, 120 deletions(-) diff --git a/src/store.js b/src/store.js index ac2d7872..f7b408f3 100644 --- a/src/store.js +++ b/src/store.js @@ -1719,126 +1719,6 @@ Scene.prototype.toXML = function (serializer) { // Sprites -/* -StageMorph.prototype.toXML = function (serializer) { - var thumbnail = normalizeCanvas( - this.thumbnail(SnapSerializer.prototype.thumbnailSize), - true - ), - thumbdata, - costumeIdx = this.getCostumeIdx(), - ide = this.parentThatIsA(IDE_Morph); - - // catch cross-origin tainting exception when using SVG costumes - try { - thumbdata = thumbnail.toDataURL('image/png'); - } catch (error) { - thumbdata = null; - } - - function code(key) { - var str = ''; - Object.keys(StageMorph.prototype[key]).forEach( - selector => { - str += ( - '<' + selector + '>' + - XML_Element.prototype.escape( - StageMorph.prototype[key][selector] - ) + - '' - ); - } - ); - return str; - } - - this.removeAllClones(); - return serializer.format( - '' + - '$' + - '$' + - '' + - '$' + - '%' + // current costume, if it's not in the wardrobe - '%' + - '%' + - '%' + - '%' + - '%%' + - '' + - '$' + - '%' + - '%' + - '%' + - '%' + - '', - (ide && ide.projectName) ? ide.projectName : localize('Untitled'), - serializer.app, - serializer.version, - (ide && ide.projectNotes) ? ide.projectNotes : '', - thumbdata, - this.name, - this.dimensions.x, - this.dimensions.y, - costumeIdx, - this.color.r, - this.color.g, - this.color.b, - this.color.a, - this.getTempo(), - this.isThreadSafe, - ide.sprites.asArray().indexOf(ide.currentSprite) + 1, - this.enablePenLogging, - this.instrument ? - ' instrument="' + parseInt(this.instrument) + '" ' : '', - this.volume, - this.pan, - SpriteMorph.prototype.useFlatLineEnds ? 'flat' : 'round', - BooleanSlotMorph.prototype.isTernary, - Process.prototype.enableHyperOps === true, - this.enableCodeMapping, - this.enableInheritance, - this.enableSublistIDs, - StageMorph.prototype.frameRate !== 0, - normalizeCanvas(this.trailsCanvas, true).toDataURL('image/png'), - - // current costume, if it's not in the wardrobe - !costumeIdx && this.costume ? - '' + serializer.store(this.costume) + '' - : '', - - serializer.store(this.costumes, this.name + '_cst'), - serializer.store(this.sounds, this.name + '_snd'), - serializer.store(this.variables), - serializer.store(this.customBlocks), - serializer.store(this.scripts), - serializer.store(this.children), - Object.keys(StageMorph.prototype.hiddenPrimitives).reduce( - (a, b) => a + ' ' + b, - '' - ), - code('codeHeaders'), - code('codeMappings'), - serializer.store(this.globalBlocks), - (ide && ide.globalVariables) ? - serializer.store(ide.globalVariables) : '' - ); -}; -*/ - StageMorph.prototype.toXML = function (serializer) { var costumeIdx = this.getCostumeIdx(), ide = this.parentThatIsA(IDE_Morph); From c56e77899689886a69e028c0b6ec2def341e4c53 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 23 Apr 2021 08:48:14 +0200 Subject: [PATCH 39/92] serialize sprite-order from scenes --- HISTORY.md | 3 +++ snap.html | 2 +- src/store.js | 16 +++++++++------- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c47caf16..74a18964 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-23 +* store: serialize sprite-order from scenes + ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects * gui, scenes: migrated "new project" feature diff --git a/snap.html b/snap.html index 34ffa557..6f3ae4ed 100755 --- a/snap.html +++ b/snap.html @@ -21,7 +21,7 @@ - + diff --git a/src/store.js b/src/store.js index f7b408f3..fa6195db 100644 --- a/src/store.js +++ b/src/store.js @@ -61,7 +61,7 @@ Project*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-April-22'; +modules.store = '2021-April-23'; // XML_Serializer /////////////////////////////////////////////////////// @@ -78,6 +78,7 @@ modules.store = '2021-April-22'; function XML_Serializer() { this.contents = []; this.media = []; + this.root = {}; this.isCollectingMedia = false; this.isExportingBlocksLibrary = false; } @@ -109,6 +110,9 @@ XML_Serializer.prototype.store = function (object, mediaID) { // when debugging, be sure to throw an error at this point return ''; } + if (object instanceof Scene) { + this.root = object; + } if (this.isCollectingMedia && object[this.mediaDetectionProperty]) { this.addMedia(object, mediaID); return this.format( @@ -174,6 +178,7 @@ XML_Serializer.prototype.flush = function () { // private - free all objects and empty my contents this.contents.forEach(obj => delete obj[this.idProperty]); this.contents = []; + this.root = {}; }; XML_Serializer.prototype.flushMedia = function () { @@ -1720,8 +1725,7 @@ Scene.prototype.toXML = function (serializer) { // Sprites StageMorph.prototype.toXML = function (serializer) { - var costumeIdx = this.getCostumeIdx(), - ide = this.parentThatIsA(IDE_Morph); + var costumeIdx = this.getCostumeIdx(); this.removeAllClones(); return serializer.format( @@ -1757,7 +1761,7 @@ StageMorph.prototype.toXML = function (serializer) { this.color.a, this.getTempo(), this.isThreadSafe, - ide.sprites.asArray().indexOf(ide.currentSprite) + 1, + serializer.root.sprites.asArray().indexOf(serializer.root.currentSprite) + 1, this.enablePenLogging, this.instrument ? ' instrument="' + parseInt(this.instrument) + '" ' : '', @@ -1787,9 +1791,7 @@ StageMorph.prototype.toXML = function (serializer) { }; SpriteMorph.prototype.toXML = function (serializer) { - var stage = this.parentThatIsA(StageMorph), - ide = stage ? stage.parentThatIsA(IDE_Morph) : null, - idx = ide ? ide.sprites.asArray().indexOf(this) + 1 : 0, + var idx = serializer.root.sprites.asArray().indexOf(this) + 1, costumeIdx = this.getCostumeIdx(), noCostumes = this.inheritsAttribute('costumes'), noSounds = this.inheritsAttribute('sounds'), From fe455ae7d67bc85441dc035fecc12ebf884aa48e Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 23 Apr 2021 10:54:39 +0200 Subject: [PATCH 40/92] sceneified refreshIDE() --- HISTORY.md | 1 + snap.html | 2 +- src/gui.js | 22 ++++++++++++---------- src/store.js | 3 ++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 74a18964..11825284 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ ### 2021-04-23 * store: serialize sprite-order from scenes +* gui: sceneified refreshIDE() ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects diff --git a/snap.html b/snap.html index 6f3ae4ed..7a360cef 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 7be1c625..6281d615 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-21'; +modules.gui = '2021-April-23'; // Declarations @@ -2587,12 +2587,12 @@ IDE_Morph.prototype.refreshIDE = function () { if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(this.scene); + projectData = this.serializer.serialize(new Project(this.scenes)); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(this.scene); + projectData = this.serializer.serialize(new Project(this.scenes)); } SpriteMorph.prototype.initBlocks(); this.buildPanes(); @@ -2785,7 +2785,9 @@ IDE_Morph.prototype.backupAndDo = function (callback) { // private var username = this.cloud.username; try { - localStorage['-snap-backup-'] = this.serializer.serialize(this.scene); + localStorage['-snap-backup-'] = this.serializer.serialize( + new Project(this.scenes) + ); delete localStorage['-snap-bakflag-']; if (username) { localStorage['-snap-bakuser-'] = username; @@ -5648,7 +5650,7 @@ IDE_Morph.prototype.toggleZebraColoring = function () { ); }; -IDE_Morph.prototype.toggleDynamicInputLabels = function () { +IDE_Morph.prototype.toggleDynamicInputLabels = function () { // +++ use refreshIDE() var projectData; SyntaxElementMorph.prototype.dynamicInputLabels = !SyntaxElementMorph.prototype.dynamicInputLabels; @@ -6014,7 +6016,7 @@ IDE_Morph.prototype.setLanguage = function (lang, callback, noSave) { translation.src = src; }; -IDE_Morph.prototype.reflectLanguage = function (lang, callback, noSave) { +IDE_Morph.prototype.reflectLanguage = function (lang, callback, noSave) { // +++ use refreshIDE() var projectData, urlBar = location.hash; SnapTranslator.language = lang; @@ -6125,7 +6127,7 @@ IDE_Morph.prototype.userSetBlocksScale = function () { ); }; -IDE_Morph.prototype.setBlocksScale = function (num) { +IDE_Morph.prototype.setBlocksScale = function (num) { // +++ use refreshIDE() var projectData; if (Process.prototype.isCatchingErrors) { try { @@ -6468,7 +6470,7 @@ IDE_Morph.prototype.logout = function () { ); }; -IDE_Morph.prototype.buildProjectRequest = function () { +IDE_Morph.prototype.buildProjectRequest = function () { // +++ Oh, sweet Jesus! var xml = this.serializer.serialize(this.scene), thumbnail = normalizeCanvas( this.stage.thumbnail( @@ -6577,7 +6579,7 @@ IDE_Morph.prototype.exportProjectMedia = function (name) { // this.hasChangedMedia = false; }; -IDE_Morph.prototype.exportProjectNoMedia = function (name) { +IDE_Morph.prototype.exportProjectNoMedia = function (name) { // +++ Sigh... var menu, str; this.serializer.isCollectingMedia = true; if (name) { @@ -6605,7 +6607,7 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { this.serializer.flushMedia(); }; -IDE_Morph.prototype.exportProjectAsCloudData = function (name) { +IDE_Morph.prototype.exportProjectAsCloudData = function (name) { // +++ revisit var menu, str, media, dta; this.serializer.isCollectingMedia = true; if (name) { diff --git a/src/store.js b/src/store.js index fa6195db..a2be8307 100644 --- a/src/store.js +++ b/src/store.js @@ -1761,7 +1761,8 @@ StageMorph.prototype.toXML = function (serializer) { this.color.a, this.getTempo(), this.isThreadSafe, - serializer.root.sprites.asArray().indexOf(serializer.root.currentSprite) + 1, + serializer.root.sprites.asArray().indexOf( + serializer.root.currentSprite) + 1, this.enablePenLogging, this.instrument ? ' instrument="' + parseInt(this.instrument) + '" ' : '', From b797189b0c057753efa80e631e1909861a9bc801 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 23 Apr 2021 11:17:52 +0200 Subject: [PATCH 41/92] sceneified toggling dynamic input labels and switching languages --- HISTORY.md | 1 + src/gui.js | 26 +++++++------------------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 11825284..691ce5d4 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,7 @@ ### 2021-04-23 * store: serialize sprite-order from scenes * gui: sceneified refreshIDE() +* gui: sceneified toggling dynamic input labels and switching languages ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects diff --git a/src/gui.js b/src/gui.js index 6281d615..737cd3b7 100644 --- a/src/gui.js +++ b/src/gui.js @@ -5650,24 +5650,10 @@ IDE_Morph.prototype.toggleZebraColoring = function () { ); }; -IDE_Morph.prototype.toggleDynamicInputLabels = function () { // +++ use refreshIDE() - var projectData; +IDE_Morph.prototype.toggleDynamicInputLabels = function () { SyntaxElementMorph.prototype.dynamicInputLabels = !SyntaxElementMorph.prototype.dynamicInputLabels; - if (Process.prototype.isCatchingErrors) { - try { - projectData = this.serializer.serialize(this.scene); - } catch (err) { - this.showMessage('Serialization failed: ' + err); - } - } else { - projectData = this.serializer.serialize(this.scene); - } - SpriteMorph.prototype.initBlocks(); - this.spriteBar.tabBar.tabTo('scripts'); - this.createCategories(); - this.createCorralBar(); - this.openProjectString(projectData); + this.refreshIDE(); }; IDE_Morph.prototype.toggleBlurredShadows = function () { @@ -6016,19 +6002,21 @@ IDE_Morph.prototype.setLanguage = function (lang, callback, noSave) { translation.src = src; }; -IDE_Morph.prototype.reflectLanguage = function (lang, callback, noSave) { // +++ use refreshIDE() +IDE_Morph.prototype.reflectLanguage = function (lang, callback, noSave) { var projectData, urlBar = location.hash; SnapTranslator.language = lang; if (!this.loadNewProject) { if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(this.scene); + projectData = this.serializer.serialize( + new Project(this.scenes) + ); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(this.scene); + projectData = this.serializer.serialize(new Project(this.scenes)); } } SpriteMorph.prototype.initBlocks(); From 2c18e60f82e497e56e450ee061265384ad53cc03 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 23 Apr 2021 11:24:59 +0200 Subject: [PATCH 42/92] sceneified "zoom blocks" --- HISTORY.md | 1 + src/gui.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 691ce5d4..c6817b7b 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,6 +12,7 @@ * store: serialize sprite-order from scenes * gui: sceneified refreshIDE() * gui: sceneified toggling dynamic input labels and switching languages +* gui: sceneified "zoom blocks" ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects diff --git a/src/gui.js b/src/gui.js index 737cd3b7..04e920bd 100644 --- a/src/gui.js +++ b/src/gui.js @@ -6115,16 +6115,16 @@ IDE_Morph.prototype.userSetBlocksScale = function () { ); }; -IDE_Morph.prototype.setBlocksScale = function (num) { // +++ use refreshIDE() +IDE_Morph.prototype.setBlocksScale = function (num) { var projectData; if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(this.scene); + projectData = this.serializer.serialize(new Project(this.scenes)); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(this.scene); + projectData = this.serializer.serialize(new Project(this.scenes)); } SyntaxElementMorph.prototype.setScale(num); CommentMorph.prototype.refreshScale(); From 1402d8227dab891e3c3b62ff67dd3ea35f02df82 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 23 Apr 2021 15:36:11 +0200 Subject: [PATCH 43/92] moved sprite-selection attribute from stage to scenes tag --- HISTORY.md | 1 + src/store.js | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c6817b7b..2d1091c4 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -13,6 +13,7 @@ * gui: sceneified refreshIDE() * gui: sceneified toggling dynamic input labels and switching languages * gui: sceneified "zoom blocks" +* store: moved sprite-selection attribute from stage to scenes tag ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects diff --git a/src/store.js b/src/store.js index a2be8307..df0f6bda 100644 --- a/src/store.js +++ b/src/store.js @@ -407,9 +407,6 @@ SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { if (model.stage.attributes.pan) { scene.stage.pan = +model.stage.attributes.pan; } - if (model.stage.attributes.select) { - scene.spriteIdx = +model.stage.attributes.select; - } if (model.stage.attributes.penlog) { scene.enablePenLogging = (model.stage.attributes.penlog === 'true'); @@ -492,8 +489,10 @@ SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { /* Sprites */ model.sprites = model.stage.require('sprites'); + if (model.sprites.attributes.select) { + scene.spriteIdx = +model.sprites.attributes.select; + } scene.spritesDict[scene.stage.name] = scene.stage; - model.sprites.childrenNamed('sprite').forEach( model => this.loadValue(model) ); @@ -1731,7 +1730,6 @@ StageMorph.prototype.toXML = function (serializer) { return serializer.format( '%' + '%' + '%' + - '%' + + '%' + '', this.dimensions.x, this.dimensions.y, @@ -1761,8 +1759,6 @@ StageMorph.prototype.toXML = function (serializer) { this.color.a, this.getTempo(), this.isThreadSafe, - serializer.root.sprites.asArray().indexOf( - serializer.root.currentSprite) + 1, this.enablePenLogging, this.instrument ? ' instrument="' + parseInt(this.instrument) + '" ' : '', @@ -1787,6 +1783,8 @@ StageMorph.prototype.toXML = function (serializer) { serializer.store(this.variables), serializer.store(this.customBlocks), serializer.store(this.scripts), + serializer.root.sprites.asArray().indexOf( + serializer.root.currentSprite) + 1, serializer.store(this.children) ); }; From 19473e2a2aa3e08b02b889bd04feaa3c8e4e7d02 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 23 Apr 2021 16:17:32 +0200 Subject: [PATCH 44/92] remember last edited scene in a project --- HISTORY.md | 1 + snap.html | 2 +- src/gui.js | 33 ++++++++++++++++++++++++--------- src/scenes.js | 15 +++++++++++++-- src/store.js | 9 +++++++-- 5 files changed, 46 insertions(+), 14 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2d1091c4..05427180 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -14,6 +14,7 @@ * gui: sceneified toggling dynamic input labels and switching languages * gui: sceneified "zoom blocks" * store: moved sprite-selection attribute from stage to scenes tag +* scenes, store, gui: remember last edited scene in a project ### 2021-04-22 * store, gui: first pass at deserializing multi-scene projects diff --git a/snap.html b/snap.html index 7a360cef..2c92a988 100755 --- a/snap.html +++ b/snap.html @@ -11,7 +11,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 04e920bd..534ab6dc 100644 --- a/src/gui.js +++ b/src/gui.js @@ -2587,12 +2587,16 @@ IDE_Morph.prototype.refreshIDE = function () { if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(new Project(this.scenes)); + projectData = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(new Project(this.scenes)); + projectData = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); } SpriteMorph.prototype.initBlocks(); this.buildPanes(); @@ -2786,7 +2790,7 @@ IDE_Morph.prototype.backupAndDo = function (callback) { var username = this.cloud.username; try { localStorage['-snap-backup-'] = this.serializer.serialize( - new Project(this.scenes) + new Project(this.scenes, this.scene) ); delete localStorage['-snap-bakflag-']; if (username) { @@ -4716,7 +4720,9 @@ IDE_Morph.prototype.exportProject = function (name, plain) { dataPrefix = 'data:text/' + plain ? 'plain,' : 'xml,'; try { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(new Project(this.scenes)); + str = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); this.setURL('#open:' + dataPrefix + encodeURIComponent(str)); this.saveXMLAs(str, name); menu.destroy(); @@ -5397,7 +5403,10 @@ IDE_Morph.prototype.openProject = function (project) { } else { this.scenes = project.scenes; } - this.switchToScene(project.scenes.at(1), true); // refresh album + this.switchToScene( + project.currentScene || project.scenes.at(1), + true // refresh album + ); }; IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { @@ -6010,13 +6019,15 @@ IDE_Morph.prototype.reflectLanguage = function (lang, callback, noSave) { if (Process.prototype.isCatchingErrors) { try { projectData = this.serializer.serialize( - new Project(this.scenes) + new Project(this.scenes, this.scene) ); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(new Project(this.scenes)); + projectData = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); } } SpriteMorph.prototype.initBlocks(); @@ -6119,12 +6130,16 @@ IDE_Morph.prototype.setBlocksScale = function (num) { var projectData; if (Process.prototype.isCatchingErrors) { try { - projectData = this.serializer.serialize(new Project(this.scenes)); + projectData = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); } catch (err) { this.showMessage('Serialization failed: ' + err); } } else { - projectData = this.serializer.serialize(new Project(this.scenes)); + projectData = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); } SyntaxElementMorph.prototype.setScale(num); CommentMorph.prototype.refreshScale(); diff --git a/src/scenes.js b/src/scenes.js index 4f98aafa..60121f9c 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -50,7 +50,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List*/ -modules.scenes = '2021-April-22'; +modules.scenes = '2021-April-23'; // Projecct ///////////////////////////////////////////////////////// @@ -60,16 +60,27 @@ modules.scenes = '2021-April-22'; // Project instance creation: -function Project(scenes) { +function Project(scenes, current) { this.name = 'Test'; this.notes = 'some notes'; this.thumbnail = null; this.scenes = scenes || new List(); + this.currentScene = current; + + // for deserializing - do not persist + this.sceneIdx = null; // for undeleting scenes - do not persist this.trash = []; } +Project.prototype.initialize = function () { + // initialize after deserializing + // only to be called by store + this.currentScene = this.scenes.at(+this.sceneIdx || 1); + return this; +}; + Project.prototype.addDefaultScene = function () { var scene = new Scene(); scene.addDefaultSprite(); diff --git a/src/store.js b/src/store.js index df0f6bda..56574ff6 100644 --- a/src/store.js +++ b/src/store.js @@ -334,13 +334,16 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide, remixID) { ); } if (scenesModel) { + if (scenesModel.attributes.select) { + project.sceneIdx = +scenesModel.attributes.select; + } scenesModel.childrenNamed('scene').forEach(model => project.scenes.add(this.loadScene(model)) ); } else { project.scenes.add(this.loadScene(xmlNode, remixID)); } - return project; + return project.initialize(); }; SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { @@ -1652,13 +1655,15 @@ Project.prototype.toXML = function (serializer) { '' + '$' + '$' + - '%' + + '%' + '', this.name || localize('Untitled'), serializer.app, serializer.version, this.notes || '', thumbdata, + this.scenes.asArray().indexOf( + this.currentScene) + 1, serializer.store(this.scenes.itemsArray()) ); }; From 639871311dd15ff70d034bd87522c62c37d862ff Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 28 Apr 2021 14:25:48 +0200 Subject: [PATCH 45/92] only show scene album if the project has more than a single scene --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 19 ++++++++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 1ca5cb91..b6721a2d 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-04-28 +* gui: only show scene album if the project has more than a single scene + ### 2021-04-23 * store: serialize sprite-order from scenes * gui: sceneified refreshIDE() diff --git a/snap.html b/snap.html index ca03a8ac..3d0d10b3 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 534ab6dc..e3514daa 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-April-23'; +modules.gui = '2021-April-28'; // Declarations @@ -1930,15 +1930,20 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { this.corral.add(this.corral.album); this.corral.fixLayout = function () { - // this.stageIcon.setCenter(this.center()); // version before scenes - this.stageIcon.setTop(this.top()); + this.stageIcon.setCenter(this.center()); this.stageIcon.setLeft(this.left() + padding); // scenes +++ - this.album.setLeft(this.left()); - this.album.setTop(this.stageIcon.bottom() + padding); - this.album.setWidth(this.stageIcon.width() + padding * 2); - this.album.setHeight(this.height() - this.stageIcon.height() - padding); + if (myself.scenes.length() < 2) { + this.album.hide(); + } else { + this.stageIcon.setTop(this.top()); + this.album.show(); + this.album.setLeft(this.left()); + this.album.setTop(this.stageIcon.bottom() + padding); + this.album.setWidth(this.stageIcon.width() + padding * 2); + this.album.setHeight(this.height() - this.stageIcon.height() - padding); // +++ + } this.frame.setLeft(this.stageIcon.right() + padding); this.frame.setExtent(new Point( From 73aef0c99e7f0ee0879308c5e7194c3abaded3d7 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Mon, 10 May 2021 16:27:36 +0200 Subject: [PATCH 46/92] project menu entries for "new scene" and "add scene" --- HISTORY.md | 3 ++ snap.html | 2 +- src/gui.js | 130 +++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 120 insertions(+), 15 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6201be41..6603ad43 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-05-10 +* gui: project menu entries for "new scene" and "add scene" + ### 2021-04-28 * gui: only show scene album if the project has more than a single scene diff --git a/snap.html b/snap.html index 74003303..88615ad2 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index a20f8037..7d016a61 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-May-04'; +modules.gui = '2021-May-10'; // Declarations @@ -241,7 +241,8 @@ IDE_Morph.prototype.init = function (isAutoFill) { // scenes this.scenes = new List([new Scene()]); this.scene = this.scenes.at(1); - this.isAddingScenes = false; // to be factored out + this.isAddingScenes = false; + this.isAddingNextScene = false; // editor this.globalVariables = this.scene.globalVariables; @@ -2348,7 +2349,23 @@ IDE_Morph.prototype.droppedAudio = function (anAudio, name) { IDE_Morph.prototype.droppedText = function (aString, name, fileType) { var lbl = name ? name.split('.')[0] : '', - ext = name ? name.slice(name.lastIndexOf('.') + 1).toLowerCase() : ''; + ext = name ? name.slice(name.lastIndexOf('.') + 1).toLowerCase() : '', + setting = this.isAddingScenes; + + // handle the special situation of adding a scene to the current project + if (this.isAddingNextScene) { + this.isAddingScenes = true; + if (aString.indexOf(' { document.body.removeChild(inp); this.filePicker = null; + if (addingScenes) { + myself.isAddingNextScene = true; // +++ + } world.hand.processDrop(inp.files); }, false @@ -4676,6 +4704,13 @@ IDE_Morph.prototype.newProject = function () { this.openProject(project); }; +IDE_Morph.prototype.createNewScene = function () { // +++ + var setting = this.isAddingScenes; + this.isAddingScenes = true; + this.newProject(); + this.isAddingScenes = setting; +}; + IDE_Morph.prototype.save = function () { // temporary hack - only allow exporting projects to disk // when running Snap! locally without a web server @@ -5138,7 +5173,7 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { IDE_Morph.prototype.openProjectString = function (str, callback) { var msg; - if (this.bulkDropInProgress) { + if (this.bulkDropInProgress || this.isAddingScenes) { // +++ this.rawOpenProjectString(str); if (callback) {callback(); } return; @@ -5185,8 +5220,12 @@ IDE_Morph.prototype.openCloudDataString = function (str) { }; IDE_Morph.prototype.rawOpenCloudDataString = function (str) { - var model; + var model, + setting = this.isAddingScenes; // +++ + if (this.isAddingNextScene) { + this.isAddingScenes = true; + } if (Process.prototype.isCatchingErrors) { try { model = this.serializer.parse(str); @@ -5213,6 +5252,8 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) { ); } this.stopFastTracking(); + this.isAddingScenes = setting; + this.isAddingNextScene = false; }; IDE_Morph.prototype.openBlocksString = function (str, name, silently) { @@ -5898,6 +5939,22 @@ IDE_Morph.prototype.createNewProject = function () { this.backup(() => this.newProject()); }; +IDE_Morph.prototype.addScene = function () { // +++ + var setting = this.isAddingScenes; + if (location.protocol === 'file:') { + // bypass the project import dialog and directly pop up + // the local file picker. + // this should not be necessary, we should be able + // to access the cloud even when running Snap! locally + // to be worked on.... (jens) + this.isAddingScenes = true; + this.importLocalFile(); + this.isAddingScenes = setting; + return; + } + new ProjectDialogMorph(this, 'add').popUp(); +}; + IDE_Morph.prototype.openProjectsBrowser = function () { if (location.protocol === 'file:') { // bypass the project import dialog and directly pop up @@ -6928,12 +6985,23 @@ ProjectDialogMorph.prototype.init = function (ide, task) { ); // override inherited properites: - this.labelString = this.task === 'save' ? 'Save Project' : 'Open Project'; + switch (this.task) { + case 'save': + this.labelString = 'Save Project'; + break; + case 'add': + this.labelString = 'Add Scene'; + break; + default: // 'open' + this.task = 'open'; + this.labelString = 'Open Project'; + } + this.createLabel(); this.key = 'project' + task; // build contents - if (task === 'open' && this.source === 'disk') { + if ((task === 'open' || task === 'add') && this.source === 'disk') { // give the user a chance to switch to another source this.source = null; this.buildContents(); @@ -6976,7 +7044,7 @@ ProjectDialogMorph.prototype.buildContents = function () { this.addSourceButton('cloud', localize('Cloud'), 'cloud'); } - if (this.task === 'open') { + if (this.task === 'open' || this.task === 'add') { this.buildFilterField(); this.addSourceButton('examples', localize('Examples'), 'poster'); if (this.hasLocalProjects() || this.ide.world().currentKey === 16) { @@ -7051,7 +7119,7 @@ ProjectDialogMorph.prototype.buildContents = function () { this.notesField.acceptsDrops = false; this.notesField.contents.acceptsDrops = false; - if (this.task === 'open') { + if (this.task === 'open' || this.task === 'add') { this.notesText = new TextMorph(''); } else { // 'save' this.notesText = new TextMorph(this.ide.projectNotes); @@ -7071,6 +7139,11 @@ ProjectDialogMorph.prototype.buildContents = function () { this.action = 'openProject'; this.recoverButton = this.addButton('recoveryDialog', 'Recover', true); this.recoverButton.hide(); + } else if (this.task === 'add') { + this.addButton('addScene', 'Add'); + this.action = 'addScene'; + this.recoverButton = this.addButton('recoveryDialog', 'Recover', true); + this.recoverButton.hide(); } else { // 'save' this.addButton('saveProject', 'Save'); this.action = 'saveProject'; @@ -7411,7 +7484,7 @@ ProjectDialogMorph.prototype.setSource = function (source) { this.shareButton.hide(); this.unshareButton.hide(); - if (this.task === 'open') { + if (this.task === 'open' || this.task === 'add') { this.recoverButton.hide(); } @@ -7424,7 +7497,7 @@ ProjectDialogMorph.prototype.setSource = function (source) { } this.buttons.fixLayout(); this.fixLayout(); - if (this.task === 'open') { + if (this.task === 'open' || this.task === 'add') { this.clearDetails(); } }; @@ -7501,7 +7574,7 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) { if (this.nameField) { this.nameField.setContents(item.projectname || ''); } - if (this.task === 'open') { + if (this.task === 'open' || this.task === 'add') { this.notesText.text = item.notes || ''; this.notesText.rerender(); this.notesField.contents.adjustBounds(); @@ -7549,7 +7622,7 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) { this.edit(); }; this.body.add(this.listField); - if (this.task === 'open') { + if (this.task === 'open' || this.task === 'add') { this.recoverButton.show(); } this.shareButton.show(); @@ -7557,7 +7630,7 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) { this.deleteButton.show(); this.buttons.fixLayout(); this.fixLayout(); - if (this.task === 'open') { + if (this.task === 'open' || this.task === 'add') { this.clearDetails(); } }; @@ -7579,6 +7652,27 @@ ProjectDialogMorph.prototype.recoveryDialog = function () { new ProjectRecoveryDialogMorph(this.ide, proj.projectname, this).popUp(); }; +ProjectDialogMorph.prototype.addScene = function () { // +++ + var proj = this.listField.selected, + src; + if (!proj) {return; } + this.ide.isAddingNextScene = true; + this.ide.source = this.source; + if (this.source === 'cloud') { + this.addCloudScene(proj); + } else if (this.source === 'examples') { + // Note "file" is a property of the parseResourceFile function. + src = this.ide.getURL(this.ide.resourceURL('Examples', proj.fileName)); + this.ide.openProjectString(src); + this.destroy(); + + } else { // 'local' + this.ide.source = null; + this.ide.openProjectName(proj.name); + this.destroy(); + } +}; + ProjectDialogMorph.prototype.openProject = function () { var proj = this.listField.selected, src; @@ -7599,6 +7693,14 @@ ProjectDialogMorph.prototype.openProject = function () { } }; +ProjectDialogMorph.prototype.addCloudScene = function (project, delta) { + // no need to backup + this.ide.nextSteps([ + () => this.ide.showMessage('Fetching project\nfrom the cloud...'), + () => this.rawOpenCloudProject(project, delta) + ]); +}; + ProjectDialogMorph.prototype.openCloudProject = function (project, delta) { this.ide.backup( () => { From 292a8a4436f1f9b78f9ff86805f62e1f763f7434 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 11 May 2021 15:14:16 +0200 Subject: [PATCH 47/92] add multi-scene projects --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 15 +++++++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6603ad43..c31806ab 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-05-11 +* gui: add multi-scene projects + ### 2021-05-10 * gui: project menu entries for "new scene" and "add scene" diff --git a/snap.html b/snap.html index 88615ad2..6e55365a 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 7d016a61..7d00746f 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-May-10'; +modules.gui = '2021-May-11'; // Declarations @@ -4209,7 +4209,7 @@ IDE_Morph.prototype.importLocalFile = function () { document.body.removeChild(inp); this.filePicker = null; if (addingScenes) { - myself.isAddingNextScene = true; // +++ + myself.isAddingNextScene = true; } world.hand.processDrop(inp.files); }, @@ -7372,7 +7372,7 @@ ProjectDialogMorph.prototype.buildFilterField = function () { // ProjectDialogMorph ops ProjectDialogMorph.prototype.setSource = function (source) { - var msg; + var msg, setting; this.source = source; this.srcBar.children.forEach(button => @@ -7409,7 +7409,14 @@ ProjectDialogMorph.prototype.setSource = function (source) { this.projectList = []; } else { this.destroy(); - this.ide.importLocalFile(); + if (this.task === 'add') { + setting = this.ide.isAddingScenes; + this.ide.isAddingScenes = true; + this.ide.importLocalFile(); + this.ide.isAddingScenes = setting; + } else { + this.ide.importLocalFile(); + } return; } break; From b5217206bd2e166ea4e34d0f3f50400d7e15d7ba Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 11 May 2021 18:39:01 +0200 Subject: [PATCH 48/92] adjusted scene album rendering --- HISTORY.md | 1 + src/gui.js | 34 ++++++++++++++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c31806ab..f3e0b2ce 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ ### 2021-05-11 * gui: add multi-scene projects +* gui: adjusted scene album rendering ### 2021-05-10 * gui: project menu entries for "new scene" and "add scene" diff --git a/src/gui.js b/src/gui.js index 7d00746f..cf603ee6 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1884,7 +1884,7 @@ IDE_Morph.prototype.createCorralBar = function () { IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { // assumes the corral bar has already been created - var frame, padding = 5, myself = this, + var frame, padding = 5, line = this.padding, myself = this, album = this.corral? this.corral.album : null; this.createStageHandle(); @@ -1925,10 +1925,14 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { this.corral.add(frame); // scenes +++ + this.corral.albumFrame = new Morph(); + this.corral.albumFrame.color = this.backgroundColor; + this.corral.add(this.corral.albumFrame); + this.corral.album = keepSceneAlbum ? album : new SceneAlbumMorph(this, this.sliderColor); - this.corral.album.color = this.frameColor; // this.groupColor; - this.corral.add(this.corral.album); + this.corral.album.color = this.groupColor; // this.frameColor; + this.corral.albumFrame.add(this.corral.album); this.corral.fixLayout = function () { this.stageIcon.setCenter(this.center()); @@ -1936,14 +1940,24 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { // scenes +++ if (myself.scenes.length() < 2) { - this.album.hide(); + this.albumFrame.hide(); } else { this.stageIcon.setTop(this.top()); - this.album.show(); + this.albumFrame.show(); + + this.albumFrame.setLeft(this.left()); + this.albumFrame.setTop(this.stageIcon.bottom() + padding); + this.albumFrame.setWidth(this.stageIcon.width() + padding * 2); + this.albumFrame.setHeight( + this.height() - this.stageIcon.height() - padding + ); + this.album.setLeft(this.left()); - this.album.setTop(this.stageIcon.bottom() + padding); - this.album.setWidth(this.stageIcon.width() + padding * 2); - this.album.setHeight(this.height() - this.stageIcon.height() - padding); // +++ + this.album.setTop(this.stageIcon.bottom() + padding + line); + this.album.setWidth(this.stageIcon.width() + padding * 2 - line); + this.album.setHeight( + this.height() - this.stageIcon.height() - padding - line + ); } this.frame.setLeft(this.stageIcon.right() + padding); @@ -10230,9 +10244,9 @@ SceneIconMorph.prototype.init = function (aScene) { var colors, action, query; colors = [ - IDE_Morph.prototype.frameColor, IDE_Morph.prototype.groupColor, - IDE_Morph.prototype.groupColor + IDE_Morph.prototype.frameColor, + IDE_Morph.prototype.frameColor ]; action = () => { From f1eacce7d234f306d5e0e9ad9e1b4f8c7f3c3a4a Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 11 May 2021 18:51:44 +0200 Subject: [PATCH 49/92] tweaked scene album rendering --- HISTORY.md | 1 + src/gui.js | 36 +++++++++++------------------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index f3e0b2ce..7287cf2d 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,7 @@ ### 2021-05-11 * gui: add multi-scene projects * gui: adjusted scene album rendering +* gui: tweaked scene album rendering ### 2021-05-10 * gui: project menu entries for "new scene" and "add scene" diff --git a/src/gui.js b/src/gui.js index cf603ee6..2460b084 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1884,7 +1884,7 @@ IDE_Morph.prototype.createCorralBar = function () { IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { // assumes the corral bar has already been created - var frame, padding = 5, line = this.padding, myself = this, + var frame, padding = 5, myself = this, album = this.corral? this.corral.album : null; this.createStageHandle(); @@ -1924,15 +1924,11 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { this.corral.frame = frame; this.corral.add(frame); - // scenes +++ - this.corral.albumFrame = new Morph(); - this.corral.albumFrame.color = this.backgroundColor; - this.corral.add(this.corral.albumFrame); - + // scenes ++++ this.corral.album = keepSceneAlbum ? album : new SceneAlbumMorph(this, this.sliderColor); - this.corral.album.color = this.groupColor; // this.frameColor; - this.corral.albumFrame.add(this.corral.album); + this.corral.album.color = this.frameColor; // this.groupColor; + this.corral.add(this.corral.album); this.corral.fixLayout = function () { this.stageIcon.setCenter(this.center()); @@ -1940,24 +1936,14 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { // scenes +++ if (myself.scenes.length() < 2) { - this.albumFrame.hide(); + this.album.hide(); } else { this.stageIcon.setTop(this.top()); - this.albumFrame.show(); - - this.albumFrame.setLeft(this.left()); - this.albumFrame.setTop(this.stageIcon.bottom() + padding); - this.albumFrame.setWidth(this.stageIcon.width() + padding * 2); - this.albumFrame.setHeight( - this.height() - this.stageIcon.height() - padding - ); - + this.album.show(); this.album.setLeft(this.left()); - this.album.setTop(this.stageIcon.bottom() + padding + line); - this.album.setWidth(this.stageIcon.width() + padding * 2 - line); - this.album.setHeight( - this.height() - this.stageIcon.height() - padding - line - ); + this.album.setTop(this.stageIcon.bottom() + padding); + this.album.setWidth(this.stageIcon.width() + padding * 2); + this.album.setHeight(this.height() - this.stageIcon.height() - padding); // +++ } this.frame.setLeft(this.stageIcon.right() + padding); @@ -10244,9 +10230,9 @@ SceneIconMorph.prototype.init = function (aScene) { var colors, action, query; colors = [ - IDE_Morph.prototype.groupColor, IDE_Morph.prototype.frameColor, - IDE_Morph.prototype.frameColor + IDE_Morph.prototype.groupColor, + IDE_Morph.prototype.groupColor ]; action = () => { From 01f82134f377aff612e32fafdc6df62e3204bcc0 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 18 May 2021 11:01:33 +0200 Subject: [PATCH 50/92] fixed exporting media only for a single scene --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7287cf2d..ae457bcc 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-05-18 +* gui: fixed exporting media only for a single scene + ### 2021-05-11 * gui: add multi-scene projects * gui: adjusted scene album rendering diff --git a/snap.html b/snap.html index 6e55365a..10f9f796 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 2460b084..7b1744d5 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-May-11'; +modules.gui = '2021-May-18'; // Declarations @@ -6626,6 +6626,7 @@ IDE_Morph.prototype.exportProjectMedia = function (name) { this.setProjectName(name); try { menu = this.showMessage('Exporting'); + this.serializer.serialize(this.scene); media = this.serializer.mediaXML(name); this.saveXMLAs(media, this.projectName + ' media'); menu.destroy(); From 5df0a5d72c76db313b74bbc4ffb626b9700a5da6 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 18 May 2021 12:14:02 +0200 Subject: [PATCH 51/92] fixed cloud file format components --- HISTORY.md | 1 + src/gui.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index ae457bcc..a4bbbe23 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ ### 2021-05-18 * gui: fixed exporting media only for a single scene +* gui: fixed cloud file format components ### 2021-05-11 * gui: add multi-scene projects diff --git a/src/gui.js b/src/gui.js index 7b1744d5..0124404f 100644 --- a/src/gui.js +++ b/src/gui.js @@ -6619,7 +6619,7 @@ IDE_Morph.prototype.saveProjectToCloud = function (name) { ); }; -IDE_Morph.prototype.exportProjectMedia = function (name) { +IDE_Morph.prototype.exportProjectMedia = function (name) { // +++ revisit for scenes var menu, media; this.serializer.isCollectingMedia = true; if (name) { @@ -6684,7 +6684,7 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { // +++ revisit str = this.serializer.serialize(this.scene); media = this.serializer.mediaXML(name); dta = '' + str + media + ''; - this.saveXMLAs(str, this.projectName); + this.saveXMLAs(dta, this.projectName); menu.destroy(); this.showMessage('Exported!', 1); } catch (err) { From 55d088e1740aa03304bd0341b4a402458fe07427 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 18 May 2021 13:04:07 +0200 Subject: [PATCH 52/92] "projectized" cloud file format for a single scene --- HISTORY.md | 1 + src/gui.js | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a4bbbe23..faf869fe 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,7 @@ ### 2021-05-18 * gui: fixed exporting media only for a single scene * gui: fixed cloud file format components +* gui: "projectized" cloud file format for a single scene ### 2021-05-11 * gui: add multi-scene projects diff --git a/src/gui.js b/src/gui.js index 0124404f..a44d6110 100644 --- a/src/gui.js +++ b/src/gui.js @@ -6626,7 +6626,7 @@ IDE_Morph.prototype.exportProjectMedia = function (name) { // +++ revisit for sc this.setProjectName(name); try { menu = this.showMessage('Exporting'); - this.serializer.serialize(this.scene); + this.serializer.serialize(new Project(this.scenes, this.scene)); media = this.serializer.mediaXML(name); this.saveXMLAs(media, this.projectName + ' media'); menu.destroy(); @@ -6653,7 +6653,9 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { // +++ Sigh... if (Process.prototype.isCatchingErrors) { try { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.scene); + str = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); this.saveXMLAs(str, this.projectName); menu.destroy(); this.showMessage('Exported!', 1); @@ -6663,7 +6665,9 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { // +++ Sigh... } } else { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.scene); + str = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); this.saveXMLAs(str, this.projectName); menu.destroy(); this.showMessage('Exported!', 1); @@ -6681,7 +6685,9 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { // +++ revisit if (Process.prototype.isCatchingErrors) { try { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.scene); + str = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); media = this.serializer.mediaXML(name); dta = '' + str + media + ''; this.saveXMLAs(dta, this.projectName); @@ -6693,7 +6699,9 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { // +++ revisit } } else { menu = this.showMessage('Exporting'); - str = this.serializer.serialize(this.scene); + str = this.serializer.serialize( + new Project(this.scenes, this.scene) + ); media = this.serializer.mediaXML(name); dta = '' + str + media + ''; this.saveXMLAs(str, this.projectName); From 33ff73aebd975a49606b0553f138d523c96caa2c Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 18 May 2021 15:55:39 +0200 Subject: [PATCH 53/92] fixed cloud file format for multi-scene projects --- HISTORY.md | 1 + src/gui.js | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index faf869fe..48c05c4c 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,6 +12,7 @@ * gui: fixed exporting media only for a single scene * gui: fixed cloud file format components * gui: "projectized" cloud file format for a single scene +* gui: fixed cloud file format for multi-scene projects ### 2021-05-11 * gui: add multi-scene projects diff --git a/src/gui.js b/src/gui.js index a44d6110..88cfa411 100644 --- a/src/gui.js +++ b/src/gui.js @@ -6535,20 +6535,24 @@ IDE_Morph.prototype.logout = function () { ); }; -IDE_Morph.prototype.buildProjectRequest = function () { // +++ Oh, sweet Jesus! - var xml = this.serializer.serialize(this.scene), - thumbnail = normalizeCanvas( +IDE_Morph.prototype.buildProjectRequest = function () { + var thumbnail = normalizeCanvas( this.stage.thumbnail( SnapSerializer.prototype.thumbnailSize )).toDataURL(), - body; + body, + xml; this.serializer.isCollectingMedia = true; + xml = this.serializer.serialize(new Project(this.scenes, this.scene)); body = { notes: this.projectNotes, xml: xml, - media: this.hasChangedMedia ? + /* + media: this.hasChangedMedia ? // incremental media upload, disabled this.serializer.mediaXML(this.projectName) : null, + */ + media: this.serializer.mediaXML(this.projectName), thumbnail: thumbnail, remixID: this.stage.remixID }; @@ -6619,7 +6623,7 @@ IDE_Morph.prototype.saveProjectToCloud = function (name) { ); }; -IDE_Morph.prototype.exportProjectMedia = function (name) { // +++ revisit for scenes +IDE_Morph.prototype.exportProjectMedia = function (name) { var menu, media; this.serializer.isCollectingMedia = true; if (name) { @@ -6645,7 +6649,7 @@ IDE_Morph.prototype.exportProjectMedia = function (name) { // +++ revisit for sc // this.hasChangedMedia = false; }; -IDE_Morph.prototype.exportProjectNoMedia = function (name) { // +++ Sigh... +IDE_Morph.prototype.exportProjectNoMedia = function (name) { var menu, str; this.serializer.isCollectingMedia = true; if (name) { @@ -6677,7 +6681,7 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { // +++ Sigh... this.serializer.flushMedia(); }; -IDE_Morph.prototype.exportProjectAsCloudData = function (name) { // +++ revisit +IDE_Morph.prototype.exportProjectAsCloudData = function (name) { var menu, str, media, dta; this.serializer.isCollectingMedia = true; if (name) { From 63d493274724fa1c92235910c7cc75ed6a0d5220 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Tue, 18 May 2021 16:24:09 +0200 Subject: [PATCH 54/92] ensured unique scene names --- HISTORY.md | 3 ++- src/gui.js | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 48c05c4c..eccb8970 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,7 +12,8 @@ * gui: fixed exporting media only for a single scene * gui: fixed cloud file format components * gui: "projectized" cloud file format for a single scene -* gui: fixed cloud file format for multi-scene projects +* gui: fixed cloud file format for multi-scene projects +* gui: ensured unique scene names ### 2021-05-11 * gui: add multi-scene projects diff --git a/src/gui.js b/src/gui.js index 88cfa411..a23be64f 100644 --- a/src/gui.js +++ b/src/gui.js @@ -3212,6 +3212,13 @@ IDE_Morph.prototype.newSpriteName = function (name, ignoredSprite) { return this.newName(name, all); }; +IDE_Morph.prototype.newSceneName = function (name, ignoredScene) { + var all = this.scenes.asArray().filter(each => + each !== ignoredScene + ).map(each => each.name); + return this.newName(name, all); +}; + IDE_Morph.prototype.newName = function (name, elements) { var ix = name.indexOf('('), stem = (ix < 0) ? name : name.substring(0, ix), @@ -5445,7 +5452,10 @@ IDE_Morph.prototype.openProjectName = function (name) { IDE_Morph.prototype.openProject = function (project) { if (this.isAddingScenes) { - project.scenes.itemsArray().forEach(scene => this.scenes.add(scene)); + project.scenes.itemsArray().forEach(scene => { + scene.name = this.newSceneName(scene.name, scene); + this.scenes.add(scene); + }); } else { this.scenes = project.scenes; } @@ -10371,13 +10381,12 @@ SceneIconMorph.prototype.userMenu = function () { SceneIconMorph.prototype.renameScene = function () { var scene = this.object, - album = this.parentThatIsA(SceneAlbumMorph), ide = this.parentThatIsA(IDE_Morph); new DialogBoxMorph( null, answer => { if (answer && (answer !== scene.name)) { - scene.name = album.scene.newSceneName( + scene.name = ide.newSceneName( answer, scene ); From 29d47422612941becc97b8fba7d2386a5fdf23d0 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 19 May 2021 16:11:57 +0200 Subject: [PATCH 55/92] disabled scene icon context menu for project scene --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 24 ++++++++++++++++-------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index eccb8970..fbe58ee7 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-05-19 +* gui: disabled scene icon context menu for project scene + ### 2021-05-18 * gui: fixed exporting media only for a single scene * gui: fixed cloud file format components diff --git a/snap.html b/snap.html index 10f9f796..a979aecb 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index a23be64f..34b6d285 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-May-18'; +modules.gui = '2021-May-19'; // Declarations @@ -10368,14 +10368,13 @@ SceneIconMorph.prototype.fixLayout // SceneIconMorph menu SceneIconMorph.prototype.userMenu = function () { - var menu = new MenuMorph(this), - ide = this.parentThatIsA(IDE_Morph); - if (!(this.object instanceof Scene)) {return null; } - menu.addItem("rename", "renameScene"); - if (ide.scenes.length() > 1) { - menu.addItem("delete", "removeScene"); + var menu = new MenuMorph(this); + if (!(this.object instanceof Scene) || this.isProjectScene()) { + return null; } - // menu.addItem("export", "exportScene"); + menu.addItem("rename", "renameScene"); + menu.addItem("delete", "removeScene"); + menu.addItem("export", "exportScene"); return menu; }; @@ -10418,6 +10417,15 @@ SceneIconMorph.prototype.exportScene = function () { ide.saveFileAs(this.object.contents.src, 'text/svg', this.object.name); }; +// SceneIconMorph ops + +SceneIconMorph.prototype.isProjectScene = function (anIDE) { + // the first scene of a project cannot be renamed, deleted or rearranged, + // because its name and project notes are those of the project + var ide = anIDE || this.parentThatIsA(IDE_Morph); + return ide.scenes.indexOf(this.object) === 1; +}; + // SceneIconMorph drawing SceneIconMorph.prototype.render From e6a81406cec673423621bef4f27705a3be568c24 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 19 May 2021 16:16:59 +0200 Subject: [PATCH 56/92] disabled dragging the project scene icon --- HISTORY.md | 1 + src/gui.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index fbe58ee7..cb2f79ad 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ ### 2021-05-19 * gui: disabled scene icon context menu for project scene +* gui: disabled dragging the project scene icon ### 2021-05-18 * gui: fixed exporting media only for a single scene diff --git a/src/gui.js b/src/gui.js index 34b6d285..bbf6dfbf 100644 --- a/src/gui.js +++ b/src/gui.js @@ -10494,8 +10494,11 @@ SceneAlbumMorph.prototype.updateList = function () { }; this.addBack(this.contents); - this.ide.scenes.asArray().forEach(scene => { + this.ide.scenes.asArray().forEach((scene, i) => { icon = new SceneIconMorph(scene); + if (i < 1) { + icon.isDraggable = false; // project scene cannot be rearranged + } icon.setPosition(new Point(x, y)); this.addContents(icon); y = icon.bottom() + padding; From 9cc238f8ef58382881c43bf5bd49796c0a778139 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 19 May 2021 16:39:46 +0200 Subject: [PATCH 57/92] made sure the project scene stays in place --- HISTORY.md | 1 + src/gui.js | 1 + 2 files changed, 2 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index cb2f79ad..1345202d 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,7 @@ ### 2021-05-19 * gui: disabled scene icon context menu for project scene * gui: disabled dragging the project scene icon +* gui: made sure the project scene stays in place ### 2021-05-18 * gui: fixed exporting media only for a single scene diff --git a/src/gui.js b/src/gui.js index bbf6dfbf..993454a3 100644 --- a/src/gui.js +++ b/src/gui.js @@ -10555,6 +10555,7 @@ SceneAlbumMorph.prototype.reactToDropOf = function (icon) { idx += 1; } }); + idx = Math.max(idx, 1); // the project scene cannot the rearranged this.ide.scenes.add(scene, idx + 1); this.updateList(); icon.mouseClickLeft(); // select From 5d58c1e20ab3f33666775cb6e705e081e67adddb Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 19 May 2021 17:57:48 +0200 Subject: [PATCH 58/92] added exporting single scenes --- HISTORY.md | 1 + src/gui.js | 33 ++++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 1345202d..a79bed36 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,6 +12,7 @@ * gui: disabled scene icon context menu for project scene * gui: disabled dragging the project scene icon * gui: made sure the project scene stays in place +* gui: added exporting single scenes ### 2021-05-18 * gui: fixed exporting media only for a single scene diff --git a/src/gui.js b/src/gui.js index 993454a3..86b868fb 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1924,7 +1924,7 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { this.corral.frame = frame; this.corral.add(frame); - // scenes ++++ + // scenes +++ this.corral.album = keepSceneAlbum ? album : new SceneAlbumMorph(this, this.sliderColor); this.corral.album.color = this.frameColor; // this.groupColor; @@ -10369,11 +10369,13 @@ SceneIconMorph.prototype.fixLayout SceneIconMorph.prototype.userMenu = function () { var menu = new MenuMorph(this); - if (!(this.object instanceof Scene) || this.isProjectScene()) { + if (!(this.object instanceof Scene)) { return null; } - menu.addItem("rename", "renameScene"); - menu.addItem("delete", "removeScene"); + if (!this.isProjectScene()) { + menu.addItem("rename", "renameScene"); + menu.addItem("delete", "removeScene"); + } menu.addItem("export", "exportScene"); return menu; }; @@ -10412,9 +10414,26 @@ SceneIconMorph.prototype.removeScene = function () { }; SceneIconMorph.prototype.exportScene = function () { - // under construction - var ide = this.parentThatIsA(IDE_Morph); - ide.saveFileAs(this.object.contents.src, 'text/svg', this.object.name); + // Export scene as project XML, saving a file to disk + var menu, str, + ide = this.parentThatIsA(IDE_Morph), + name = this.object.name || localize('untitled'); + + try { + menu = ide.showMessage('Exporting'); + str = ide.serializer.serialize( + new Project(new List([this.object]), this.object) + ); + ide.saveXMLAs(str, name); + menu.destroy(); + ide.showMessage('Exported!', 1); + } catch (err) { + if (Process.prototype.isCatchingErrors) { + ide.showMessage('Export failed: ' + err); + } else { + throw err; + } + } }; // SceneIconMorph ops From 2c0760135456767d61d88c32b025f93d8e967c1b Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 19 May 2021 19:04:18 +0200 Subject: [PATCH 59/92] removed redundant properties "notes" and "thumbnail" from project --- HISTORY.md | 1 + snap.html | 4 ++-- src/scenes.js | 11 +++++++---- src/store.js | 15 +-------------- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a79bed36..40a4a5c0 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -13,6 +13,7 @@ * gui: disabled dragging the project scene icon * gui: made sure the project scene stays in place * gui: added exporting single scenes +* scenes, store: removed redundant properties "notes" and "thumbnail" from project ### 2021-05-18 * gui: fixed exporting media only for a single scene diff --git a/snap.html b/snap.html index a979aecb..33e9e700 100755 --- a/snap.html +++ b/snap.html @@ -11,7 +11,7 @@ - + @@ -21,7 +21,7 @@ - + diff --git a/src/scenes.js b/src/scenes.js index 60121f9c..f7bd29c9 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -50,7 +50,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List*/ -modules.scenes = '2021-April-23'; +modules.scenes = '2021-May-19'; // Projecct ///////////////////////////////////////////////////////// @@ -61,12 +61,15 @@ modules.scenes = '2021-April-23'; // Project instance creation: function Project(scenes, current) { - this.name = 'Test'; - this.notes = 'some notes'; - this.thumbnail = null; + var projectScene; + this.scenes = scenes || new List(); this.currentScene = current; + // proxied for display + projectScene = this.scenes.at(1); + this.name = projectScene ? projectScene.name : null; + // for deserializing - do not persist this.sceneIdx = null; diff --git a/src/store.js b/src/store.js index 56574ff6..175e2b53 100644 --- a/src/store.js +++ b/src/store.js @@ -61,7 +61,7 @@ Project*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-April-23'; +modules.store = '2021-May-19'; // XML_Serializer /////////////////////////////////////////////////////// @@ -1642,26 +1642,13 @@ Array.prototype.toXML = function (serializer) { // Scenes & multi-scene projects Project.prototype.toXML = function (serializer) { - var thumbdata; - - // thumb data catch cross-origin tainting exception when using SVG costumes - try { - thumbdata = this.thumbnail.toDataURL('image/png'); - } catch (error) { - thumbdata = null; - } - return serializer.format( '' + - '$' + - '$' + '%' + '', this.name || localize('Untitled'), serializer.app, serializer.version, - this.notes || '', - thumbdata, this.scenes.asArray().indexOf( this.currentScene) + 1, serializer.store(this.scenes.itemsArray()) From 9b35d468de21169c9f5beb4ddf8a70d9d6040588 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 19 May 2021 19:49:46 +0200 Subject: [PATCH 60/92] removed "thumbnail" property from scene xml --- src/store.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/store.js b/src/store.js index 175e2b53..2bdb9fec 100644 --- a/src/store.js +++ b/src/store.js @@ -1657,7 +1657,6 @@ Project.prototype.toXML = function (serializer) { Scene.prototype.toXML = function (serializer) { var tmp = new Scene(), - thumbdata, xml; function code(key) { @@ -1676,19 +1675,11 @@ Scene.prototype.toXML = function (serializer) { return str; } - // catch cross-origin tainting exception when using SVG costumes - try { - thumbdata = this.thumbnail.toDataURL('image/png'); - } catch (error) { - thumbdata = null; - } - tmp.captureGlobalSettings(); this.applyGlobalSettings(); xml = serializer.format( '' + '$' + - '$' + '$' + '%' + '%' + @@ -1698,7 +1689,6 @@ Scene.prototype.toXML = function (serializer) { '', this.name || localize('Untitled'), this.notes || '', - thumbdata, Object.keys(StageMorph.prototype.hiddenPrimitives).reduce( (a, b) => a + ' ' + b, '' From 6756c920bcbd6b6e3b6610b47b5e1a55821a65f2 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Wed, 19 May 2021 19:49:54 +0200 Subject: [PATCH 61/92] Update HISTORY.md --- HISTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.md b/HISTORY.md index 40a4a5c0..433a2a27 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -14,6 +14,7 @@ * gui: made sure the project scene stays in place * gui: added exporting single scenes * scenes, store: removed redundant properties "notes" and "thumbnail" from project +* store: removed "thumbnail" property from scene xml ### 2021-05-18 * gui: fixed exporting media only for a single scene From ae13edd984a5217a80e7c907b28567fca88c93c3 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Thu, 20 May 2021 23:22:12 +0200 Subject: [PATCH 62/92] marked projectName to be refactored and sceneified --- HISTORY.md | 3 +++ snap.html | 2 +- src/gui.js | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 433a2a27..6c9af651 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-05-20 +* gui: marked projectName to be refactored and sceneified + ### 2021-05-19 * gui: disabled scene icon context menu for project scene * gui: disabled dragging the project scene icon diff --git a/snap.html b/snap.html index 33e9e700..f5f65f52 100755 --- a/snap.html +++ b/snap.html @@ -12,7 +12,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 86b868fb..4d070861 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-May-19'; +modules.gui = '2021-May-20'; // Declarations @@ -2113,7 +2113,7 @@ IDE_Morph.prototype.fixLayout = function (situation) { } }; -IDE_Morph.prototype.setProjectName = function (string) { +IDE_Morph.prototype.setProjectName = function (string) { // ++++ change to scene name / distinguish scene name an project name this.projectName = string.replace(/['"]/g, ''); // filter quotation marks this.hasChangedMedia = true; this.controlBar.updateLabel(); From 498c4c8edf3d766a77fb98a9c110165b27867d85 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 00:08:24 +0200 Subject: [PATCH 63/92] proxied thumbnail, name and notes in project, restored in XML --- HISTORY.md | 3 +++ snap.html | 6 +++--- src/gui.js | 25 +++++++++++-------------- src/scenes.js | 17 ++++++++++++++--- src/store.js | 15 ++++++++++++++- 5 files changed, 45 insertions(+), 21 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6c9af651..4a3589c6 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -8,6 +8,9 @@ * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-05-21 +* gui, scenes, store: proxied thumbnail, name and notes in project, restored in XML + ### 2021-05-20 * gui: marked projectName to be refactored and sceneified diff --git a/snap.html b/snap.html index f5f65f52..aab7a08b 100755 --- a/snap.html +++ b/snap.html @@ -11,8 +11,8 @@ - - + + @@ -21,7 +21,7 @@ - + diff --git a/src/gui.js b/src/gui.js index 4d070861..6078961e 100644 --- a/src/gui.js +++ b/src/gui.js @@ -68,7 +68,7 @@ /*global modules, Morph, SpriteMorph, SyntaxElementMorph, Color, Cloud, Audio, ListWatcherMorph, TextMorph, newCanvas, useBlurredShadows, Sound, Scene, Note, -StringMorph, Point, MenuMorph, morphicVersion, DialogBoxMorph, normalizeCanvas, +StringMorph, Point, MenuMorph, morphicVersion, DialogBoxMorph, BlockEditorMorph, ToggleButtonMorph, contains, ScrollFrameMorph, StageMorph, PushButtonMorph, sb, InputFieldMorph, FrameMorph, Process, nop, SnapSerializer, ListMorph, detect, AlignmentMorph, TabMorph, Costume, MorphicPreferences,BlockMorph, ToggleMorph, @@ -79,11 +79,11 @@ CommandBlockMorph, BooleanSlotMorph, RingReporterSlotMorph, ScriptFocusMorph, BlockLabelPlaceHolderMorph, SpeechBubbleMorph, XML_Element, WatcherMorph, WHITE, BlockRemovalDialogMorph,TableMorph, isSnapObject, isRetinaEnabled, SliderMorph, disableRetinaSupport, enableRetinaSupport, isRetinaSupported, MediaRecorder, -Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Project, ZERO, BLACK*/ +Animation, BoxMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-May-20'; +modules.gui = '2021-May-21'; // Declarations @@ -4760,7 +4760,7 @@ IDE_Morph.prototype.exportProject = function (name, plain) { // newWindow requests displaying the project in a new tab. var menu, str, dataPrefix; - name = this.scenes.at(1).name; // +++ + name = this.scenes.at(1).name; // +++++ if (name) { this.setProjectName(name); @@ -6546,25 +6546,22 @@ IDE_Morph.prototype.logout = function () { }; IDE_Morph.prototype.buildProjectRequest = function () { - var thumbnail = normalizeCanvas( - this.stage.thumbnail( - SnapSerializer.prototype.thumbnailSize - )).toDataURL(), + var proj = new Project(this.scenes, this.scene), body, xml; this.serializer.isCollectingMedia = true; - xml = this.serializer.serialize(new Project(this.scenes, this.scene)); + xml = this.serializer.serialize(proj); body = { - notes: this.projectNotes, + notes: proj.notes, xml: xml, /* media: this.hasChangedMedia ? // incremental media upload, disabled - this.serializer.mediaXML(this.projectName) : null, + this.serializer.mediaXML(proj.name) : null, */ - media: this.serializer.mediaXML(this.projectName), - thumbnail: thumbnail, - remixID: this.stage.remixID + media: this.serializer.mediaXML(proj.name), + thumbnail: proj.thumbnail.toDataURL(), + remixID: this.stage.remixID // +++ sceneify remixID }; this.serializer.isCollectingMedia = false; this.serializer.flushMedia(); diff --git a/src/scenes.js b/src/scenes.js index f7bd29c9..52e9a8d6 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -48,9 +48,10 @@ // Global stuff //////////////////////////////////////////////////////// -/*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List*/ +/*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List, +normalizeCanvas, SnapSerializer*/ -modules.scenes = '2021-May-19'; +modules.scenes = '2021-May-21'; // Projecct ///////////////////////////////////////////////////////// @@ -67,8 +68,18 @@ function Project(scenes, current) { this.currentScene = current; // proxied for display + this.name = null; + this.notes = null; + this.thumbnail = null; + projectScene = this.scenes.at(1); - this.name = projectScene ? projectScene.name : null; + if (projectScene) { + this.name = projectScene.name; + this.notes = projectScene.notes; + this.thumbnail = normalizeCanvas( + projectScene.stage.thumbnail(SnapSerializer.prototype.thumbnailSize) + ); + } // for deserializing - do not persist this.sceneIdx = null; diff --git a/src/store.js b/src/store.js index 2bdb9fec..fd1f8357 100644 --- a/src/store.js +++ b/src/store.js @@ -61,7 +61,7 @@ Project*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-May-19'; +modules.store = '2021-May-21'; // XML_Serializer /////////////////////////////////////////////////////// @@ -1642,13 +1642,26 @@ Array.prototype.toXML = function (serializer) { // Scenes & multi-scene projects Project.prototype.toXML = function (serializer) { + var thumbdata; + + // thumb data catch cross-origin tainting exception when using SVG costumes + try { + thumbdata = this.thumbnail.toDataURL('image/png'); + } catch (error) { + thumbdata = null; + } + return serializer.format( '' + + '$' + + '$' + '%' + '', this.name || localize('Untitled'), serializer.app, serializer.version, + this.notes || '', + thumbdata, this.scenes.asArray().indexOf( this.currentScene) + 1, serializer.store(this.scenes.itemsArray()) From 58f0a6c918d59b5637319a47b84d7e6d122fff12 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Thu, 20 May 2021 12:24:05 -1000 Subject: [PATCH 64/92] First Draft: Support a unified palette. * A scene now has a setting for "unified palette" * hides the category selectors * shows all blocks in the palette * tbd how to be use the space. --- src/gui.js | 47 +++++++++++++++-- src/objects.js | 135 ++++++++++++++++++++++--------------------------- src/scenes.js | 3 ++ src/store.js | 2 + 4 files changed, 108 insertions(+), 79 deletions(-) diff --git a/src/gui.js b/src/gui.js index 2460b084..e66d12da 100644 --- a/src/gui.js +++ b/src/gui.js @@ -250,7 +250,11 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.sprites = this.scene.sprites; this.projectName = this.scene.name; this.projectNotes = this.scene.notes; - this.currentCategory = 'motion'; + if (this.scene.unifiedPalette) { + this.currentCategory = 'unified'; + } else { + this.currentCategory = 'motion'; + } this.currentTab = 'scripts'; // logoURL is disabled because the image data is hard-copied @@ -393,7 +397,7 @@ IDE_Morph.prototype.openIn = function (world) { world.worldCanvas.focus(); } } - + function autoRun () { // wait until all costumes and sounds are loaded if (isLoadingAssets()) { @@ -1228,6 +1232,17 @@ IDE_Morph.prototype.createCategories = function () { this.categories.bounds.setWidth(this.paletteWidth); // this.categories.getRenderColor = ScriptsMorph.prototype.getRenderColor; + if (this.scene.unifiedPalette) { + // TODO: What should this look like? + // Ensure there's a min height so the Sprite toolbar has a correct layout. + // Include the search bar + make a block button here? + this.categories.bounds.setHeight(84); + let wastedSpace = new Morph(); + wastedSpace.setExtent(new Point(this.paletteWidth, 84)); + this.categories.add(wastedSpace); + this.add(this.categories); + return; + } function addCategoryButton(category) { var labelWidth = 75, colors = [ @@ -1313,11 +1328,13 @@ IDE_Morph.prototype.createCategories = function () { IDE_Morph.prototype.createPalette = function (forSearching) { // assumes that the logo pane has already been created // needs the categories pane for layout + let unifiedPalette = this.scene.unifiedPalette; if (this.palette) { this.palette.destroy(); } + // TODO: Always show if unified? if (forSearching) { this.palette = new ScrollFrameMorph( null, @@ -2298,7 +2315,7 @@ IDE_Morph.prototype.droppedSVG = function (anImage, name) { // all the images are: // sized, with 'width' and 'height' attributes // fitted to stage dimensions - // and with their 'viewBox' attribute + // and with their 'viewBox' attribute if (normalizing) { svgNormalized = new Image(w, h); svgObj.setAttribute('width', w); @@ -3914,6 +3931,28 @@ IDE_Morph.prototype.settingsMenu = function () { 'check to enable\nusing operators on lists and tables', false ); + addPreference( + 'Unified Palette', + () => { + this.scene.unifiedPalette = !this.scene.unifiedPalette; + if (this.scene.unifiedPalette) { + this.currentCategory = 'unified'; + } else { + this.currentCategory = 'motion'; + } + this.createCategories(); + this.categories.fixLayout(); + this.fixLayout(); + this.flushBlocksCache(); + this.flushPaletteCache(); + this.currentSprite.palette(this.currentCategory); + this.refreshPalette(true); + }, + this.scene.unifiedPalette, + 'uncheck to disable\nusing operators on lists and tables', + 'check to enable\nusing operators on lists and tables', + false + ); addPreference( 'Persist linked sublist IDs', () => StageMorph.prototype.enableSublistIDs = @@ -11014,7 +11053,7 @@ SoundRecorderDialogMorph.prototype.buildContents = function () { audio: { channelCount: 1 // force mono, currently only works on FF } - + } ).then(stream => { this.mediaRecorder = new MediaRecorder(stream); diff --git a/src/objects.js b/src/objects.js index aecaddec..e1ab0588 100644 --- a/src/objects.js +++ b/src/objects.js @@ -1693,7 +1693,7 @@ SpriteMorph.prototype.blockAlternatives = { changeBackgroundHSVA: ['setBackgroundHSVA'], changeSize: ['setSize'], setSize: ['changeSize'], - + // control: doBroadcast: ['doBroadcastAndWait', 'doSend'], doBroadcastAndWait: ['doBroadcast', 'doSend'], @@ -2358,7 +2358,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { } } - if (cat === 'motion') { + if (cat === 'motion' || cat === 'unified') { blocks.push(block('forward')); blocks.push(block('turn')); @@ -2384,10 +2384,9 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('yPosition', this.inheritsAttribute('y position'))); blocks.push(watcherToggle('direction')); blocks.push(block('direction', this.inheritsAttribute('direction'))); - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'looks') { + } + if (cat === 'looks' || cat === 'unified') { blocks.push(block('doSwitchToCostume')); blocks.push(block('doWearNextCostume')); @@ -2423,7 +2422,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('doSwitchToScene')); - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); @@ -2440,12 +2439,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doScreenshot')); } - ///////////////////////////////// - - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - - } else if (cat === 'sound') { + } + if (cat === 'sound' || cat === 'unified') { blocks.push(block('playSound')); blocks.push(block('doPlaySoundUntilDone')); @@ -2477,7 +2472,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('playFreq')); blocks.push(block('stopFreq')); - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); @@ -2491,12 +2486,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doPlayFrequency')); } - ///////////////////////////////// - - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - - } else if (cat === 'pen') { + } + if (cat === 'pen' || cat === 'unified') { blocks.push(block('clear')); blocks.push('-'); @@ -2521,10 +2512,9 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('doPasteOn')); blocks.push(block('doCutFrom')); - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'control') { + } + if (cat === 'control' || cat === 'unified') { blocks.push(block('receiveGo')); blocks.push(block('receiveKey')); @@ -2571,10 +2561,9 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('removeClone')); blocks.push('-'); blocks.push(block('doPauseAll')); - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'sensing') { + } + if (cat === 'sensing' || cat === 'unified') { blocks.push(block('reportTouchingObject')); blocks.push(block('reportTouchingColor')); @@ -2617,7 +2606,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('reportDate')); - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { @@ -2636,12 +2625,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportYieldCount')); } - ///////////////////////////////// - - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - - } else if (cat === 'operators') { + } + if (cat === 'operators' || cat === 'unified') { blocks.push(block('reifyScript')); blocks.push(block('reifyReporter')); @@ -2687,7 +2672,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { } } - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); @@ -2702,12 +2687,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportTextFunction')); } - ///////////////////////////////// - - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - - } else if (cat === 'variables') { + } + if (cat === 'variables' || cat === 'unified') { button = new PushButtonMorph( null, @@ -2787,14 +2768,14 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doHideVar')); blocks.push(block('doDeclareVariables')); - // inheritance: + // inheritance: if (StageMorph.prototype.enableInheritance) { blocks.push('-'); blocks.push(block('doDeleteAttr')); } - /////////////////////////////// + /////////////////////////////// blocks.push('='); @@ -2825,7 +2806,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doInsertInList')); blocks.push(block('doReplaceInList')); - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); @@ -2839,21 +2820,23 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doShowTable')); } - ///////////////////////////////// - - blocks.push('='); - if (StageMorph.prototype.enableCodeMapping) { + blocks.push('='); blocks.push(block('doMapCodeOrHeader')); blocks.push(block('doMapValueCode')); blocks.push(block('doMapListCode')); blocks.push('-'); blocks.push(block('reportMappedCode')); - blocks.push('='); } + } + + if (cat !== ' unified') { + // TODO: Should probably show thos. + // what should category default to? + blocks.push('='); + blocks.push(this.makeBlockButton(cat)); + } - blocks.push(this.makeBlockButton()); - } return blocks; }; @@ -2941,7 +2924,7 @@ SpriteMorph.prototype.freshPalette = function (category) { palette.growth = new Point(0, MorphicPreferences.scrollBarSize); // toolbar: - + palette.toolBar = new AlignmentMorph('column'); searchButton = new PushButtonMorph( @@ -6002,7 +5985,7 @@ SpriteMorph.prototype.xRight = function () { } return this.right(); }; - + SpriteMorph.prototype.yTop = function () { var stage = this.parentThatIsA(StageMorph); @@ -6289,7 +6272,7 @@ SpriteMorph.prototype.toggleVariableWatcher = function (varName, isGlobal) { globals = this.globalVariables(), watcher, others; - + if (stage === null) { return null; } @@ -8599,7 +8582,7 @@ StageMorph.prototype.blockTemplates = function (category) { } } - if (cat === 'motion') { + if (cat === 'motion' || cat === 'unified') { txt = new TextMorph(localize( 'Stage selected:\nno motion primitives' @@ -8610,7 +8593,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('='); blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'looks') { + } + if (cat === 'looks' || cat === 'unified') { blocks.push(block('doSwitchToCostume')); blocks.push(block('doWearNextCostume')); @@ -8633,8 +8617,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('doSwitchToScene')); - // for debugging: /////////////// - + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); txt = new TextMorph(localize( @@ -8650,12 +8633,12 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doScreenshot')); } - ///////////////////////////////// - + ///////////////////////////////// blocks.push('='); blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'sound') { + } + if (cat === 'sound' || cat === 'unified') { blocks.push(block('playSound')); blocks.push(block('doPlaySoundUntilDone')); @@ -8687,8 +8670,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('playFreq')); blocks.push(block('stopFreq')); - // for debugging: /////////////// - + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); txt = new TextMorph(localize( @@ -8701,12 +8683,12 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doPlayFrequency')); } - ///////////////////////////////// - + ///////////////////////////////// blocks.push('='); blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'pen') { + } + if (cat === 'pen' || cat === 'unified') { blocks.push(block('clear')); blocks.push('-'); @@ -8721,7 +8703,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('='); blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'control') { + } + if (cat === 'control' || cat === 'unified') { blocks.push(block('receiveGo')); blocks.push(block('receiveKey')); @@ -8769,7 +8752,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('='); blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'sensing') { + } + if (cat === 'sensing' || cat === 'unified') { blocks.push(block('doAsk')); blocks.push(watcherToggle('getLastAnswer')); @@ -8807,7 +8791,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('reportDate')); - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { @@ -8826,12 +8810,13 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportYieldCount')); } - ///////////////////////////////// + ///////////////////////////////// blocks.push('='); blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'operators') { + } + if (cat === 'operators' || cat === 'unified') { blocks.push(block('reifyScript')); blocks.push(block('reifyReporter')); @@ -8877,7 +8862,7 @@ StageMorph.prototype.blockTemplates = function (category) { } } - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); @@ -8892,12 +8877,12 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportTextFunction')); } - ////////////////////////////////// - + ////////////////////////////////// blocks.push('='); blocks.push(this.makeBlockButton(cat)); - } else if (cat === 'variables') { + } + if (cat === 'variables' || cat === 'unified') { button = new PushButtonMorph( null, @@ -8998,7 +8983,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doInsertInList')); blocks.push(block('doReplaceInList')); - // for debugging: /////////////// + // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); @@ -9012,7 +8997,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doShowTable')); } - ///////////////////////////////// + //////// ///////////////////////// blocks.push('='); diff --git a/src/scenes.js b/src/scenes.js index 60121f9c..fb08842e 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -111,6 +111,7 @@ function Scene(aStageMorph) { this.hiddenPrimitives = {}; this.codeMappings = {}; this.codeHeaders = {}; + this.unifiedPalette = false; // global settings (copied) this.enableCodeMapping = false; @@ -164,6 +165,7 @@ Scene.prototype.addDefaultSprite = function () { Scene.prototype.captureGlobalSettings = function () { this.hiddenPrimitives = StageMorph.prototype.hiddenPrimitives; + this.unifiedPalette = StageMorph.prototype.unifiedPalette; this.codeMappings = StageMorph.prototype.codeMappings; this.codeHeaders = StageMorph.prototype.codeHeaders; this.enableCodeMapping = StageMorph.prototype.enableCodeMapping; @@ -177,6 +179,7 @@ Scene.prototype.captureGlobalSettings = function () { Scene.prototype.applyGlobalSettings = function () { StageMorph.prototype.hiddenPrimitives = this.hiddenPrimitives; + StageMorph.prototype.unifiedPalette = this.unifiedPalette; StageMorph.prototype.codeMappings = this.codeMappings; StageMorph.prototype.codeHeaders = this.codeHeaders; StageMorph.prototype.enableCodeMapping = this.enableCodeMapping; diff --git a/src/store.js b/src/store.js index 56574ff6..32d8b084 100644 --- a/src/store.js +++ b/src/store.js @@ -453,6 +453,8 @@ SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { scene.enableSublistIDs = model.stage.attributes.sublistIDs === 'true'; + scene.unifiedPalette = model.stage.unifiedPalette === 'true'; + model.hiddenPrimitives = model.scene.childNamed('hidden'); if (model.hiddenPrimitives) { model.hiddenPrimitives.contents.split(' ').forEach( From 45376a2977196db7f655ccc33a2308d683a555c0 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 14:28:03 +0200 Subject: [PATCH 65/92] distinguished project name from scene names also removed hidden "export as plain text" option --- HISTORY.md | 1 + src/gui.js | 152 +++++++++++++++++++++++++---------------------------- 2 files changed, 73 insertions(+), 80 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 4a3589c6..9179c663 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ ### 2021-05-21 * gui, scenes, store: proxied thumbnail, name and notes in project, restored in XML +* gui: distinguished project name from scene names, removed hidden "export as plain text" option ### 2021-05-20 * gui: marked projectName to be refactored and sceneified diff --git a/src/gui.js b/src/gui.js index 6078961e..bc528c6a 100644 --- a/src/gui.js +++ b/src/gui.js @@ -248,8 +248,7 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.globalVariables = this.scene.globalVariables; this.currentSprite = this.scene.addDefaultSprite(); this.sprites = this.scene.sprites; - this.projectName = this.scene.name; - this.projectNotes = this.scene.notes; + this.projectNotes = this.scene.notes; // +++ sceneify rename to scene notes this.currentCategory = 'motion'; this.currentTab = 'scripts'; @@ -1189,7 +1188,7 @@ IDE_Morph.prototype.createControlBar = function () { return; } txt = new StringMorph( - prefix + (myself.projectName || localize('untitled')) + suffix, + prefix + (myself.scene.name || localize('untitled')) + suffix, 14, 'sans-serif', true, @@ -2113,10 +2112,24 @@ IDE_Morph.prototype.fixLayout = function (situation) { } }; -IDE_Morph.prototype.setProjectName = function (string) { // ++++ change to scene name / distinguish scene name an project name - this.projectName = string.replace(/['"]/g, ''); // filter quotation marks - this.hasChangedMedia = true; - this.controlBar.updateLabel(); +// IDE_Morph project name + +IDE_Morph.prototype.getProjectName = function () { + return this.scenes.at(1).name; +}; + +IDE_Morph.prototype.setProjectName = function (string) { + var projectScene = this.scenes.at(1), + name = this.newSceneName(string, projectScene); + if (name !== projectScene.name) { + projectScene.name = name; + projectScene.stage.version = Date.now(); + this.hasChangedMedia = true; // +++ sceneify this + if (projectScene === this.scene) { + this.controlBar.updateLabel(); + } + } + return name; }; // IDE_Morph resizing @@ -3213,10 +3226,11 @@ IDE_Morph.prototype.newSpriteName = function (name, ignoredSprite) { }; IDE_Morph.prototype.newSceneName = function (name, ignoredScene) { - var all = this.scenes.asArray().filter(each => + var sName = name.replace(/['"]/g, ''), // filter out quotation marks + all = this.scenes.asArray().filter(each => each !== ignoredScene ).map(each => each.name); - return this.newName(name, all); + return this.newName(sName, all); }; IDE_Morph.prototype.newName = function (name, elements) { @@ -3362,8 +3376,9 @@ IDE_Morph.prototype.cloudMenu = function () { menu.addItem( 'export project media only...', () => { - if (this.projectName) { - this.exportProjectMedia(this.projectName); + var pn = this.getProjectName(); + if (pn) { + this.exportProjectMedia(pn); } else { this.prompt( 'Export Project As...', @@ -3379,8 +3394,9 @@ IDE_Morph.prototype.cloudMenu = function () { menu.addItem( 'export project without media...', () => { - if (this.projectName) { - this.exportProjectNoMedia(this.projectName); + var pn = this.getProjectName(); + if (pn) { + this.exportProjectNoMedia(pn); } else { this.prompt( 'Export Project As...', @@ -3396,8 +3412,9 @@ IDE_Morph.prototype.cloudMenu = function () { menu.addItem( 'export project as cloud data...', () => { - if (this.projectName) { - this.exportProjectAsCloudData(this.projectName); + var pn = this.getProjectName(); + if (pn) { + this.exportProjectAsCloudData(pn); } else { this.prompt( 'Export Project As...', @@ -3983,46 +4000,22 @@ IDE_Morph.prototype.projectMenu = function () { 'importLocalFile', 'file menu import hint' // looks up the actual text in the translator ); - - if (shiftClicked) { - menu.addItem( - localize( - 'Export project...') + ' ' + localize('(in a new window)' - ), - () => { - if (this.projectName) { - this.exportProject(this.projectName, shiftClicked); - } else { - this.prompt( - 'Export Project As...', - // false - override the shiftClick setting to use XML: - name => this.exportProject(name, false), - null, - 'exportProject' - ); - } - }, - 'show project data as XML\nin a new browser window', - new Color(100, 0, 0) - ); - } menu.addItem( - shiftClicked ? - 'Export project as plain text...' : 'Export project...', + 'Export project...', () => { - if (this.projectName) { - this.exportProject(this.projectName, shiftClicked); + var pn = this.getProjectName(); + if (this.pn) { + this.exportProject(pn); } else { this.prompt( 'Export Project As...', - name => this.exportProject(name, shiftClicked), + name => this.exportProject(name), null, 'exportProject' ); } }, - 'save project data as XML\nto your downloads folder', - shiftClicked ? new Color(100, 0, 0) : null + 'save project data as XML\nto your downloads folder' ); if (this.stage.globalBlocks.length) { @@ -4721,13 +4714,14 @@ IDE_Morph.prototype.createNewScene = function () { // +++ IDE_Morph.prototype.save = function () { // temporary hack - only allow exporting projects to disk // when running Snap! locally without a web server + var pn = this.getProjectName(); if (location.protocol === 'file:') { - if (this.projectName) { - this.exportProject(this.projectName, false); + if (pn) { + this.exportProject(pn); } else { this.prompt( 'Export Project As...', - name => this.exportProject(name, false), + name => this.exportProject(name), null, 'exportProject' ); @@ -4742,11 +4736,11 @@ IDE_Morph.prototype.save = function () { if (this.cloud.disabled) {this.source = 'disk'; } - if (this.projectName) { + if (pn) { if (this.source === 'disk') { - this.exportProject(this.projectName); + this.exportProject(pn); } else if (this.source === 'cloud') { - this.saveProjectToCloud(this.projectName); + this.saveProjectToCloud(pn); } else { this.saveProjectsBrowser(); } @@ -4755,22 +4749,17 @@ IDE_Morph.prototype.save = function () { } }; -IDE_Morph.prototype.exportProject = function (name, plain) { +IDE_Morph.prototype.exportProject = function (name) { // Export project XML, saving a file to disk - // newWindow requests displaying the project in a new tab. - var menu, str, dataPrefix; - - name = this.scenes.at(1).name; // +++++ - + var menu, str; if (name) { - this.setProjectName(name); - dataPrefix = 'data:text/' + plain ? 'plain,' : 'xml,'; + name = this.setProjectName(name); try { menu = this.showMessage('Exporting'); str = this.serializer.serialize( new Project(this.scenes, this.scene) ); - this.setURL('#open:' + dataPrefix + encodeURIComponent(str)); + this.setURL('#open:data:text/xml,' + encodeURIComponent(str)); this.saveXMLAs(str, name); menu.destroy(); this.recordSavedChanges(); @@ -4896,10 +4885,10 @@ IDE_Morph.prototype.exportScriptsPicture = function () { y += padding; y += each.height; }); - this.saveCanvasAs(pic, this.projectName || localize('Untitled')); + this.saveCanvasAs(pic, this.scene.name || localize('Untitled')); }; -IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { +IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { // ++++ sceneify: make it "scene summary" and include it in the scene icon context menu var html, head, meta, css, body, pname, notes, toc, globalVars, stage = this.stage; @@ -4999,7 +4988,7 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { } } - pname = this.projectName || localize('untitled'); + pname = this.getProjectName() || localize('untitled'); // ++++ sceneify html = new XML_Element('html'); html.attributes.lang = SnapTranslator.language; @@ -5474,7 +5463,7 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { ); this.scene.captureGlobalSettings(); this.scene = scene; - this.projectName = scene.name; + // +++ this.projectName = scene.name; // remove this.projectNotes = scene.notes || ''; this.globalVariables = scene.globalVariables; this.stage.destroy(); @@ -5984,7 +5973,7 @@ IDE_Morph.prototype.saveProjectsBrowser = function () { if (location.protocol === 'file:') { this.prompt( 'Export Project As...', - name => this.exportProject(name, false), + name => this.exportProject(name), null, 'exportProject' ); @@ -6609,7 +6598,7 @@ IDE_Morph.prototype.saveProjectToCloud = function (name) { var projectBody, projectSize; if (name) { - this.setProjectName(name); + name = this.setProjectName(name); } this.showMessage('Saving project\nto the cloud...'); @@ -6620,7 +6609,7 @@ IDE_Morph.prototype.saveProjectToCloud = function (name) { 'Uploading ' + Math.round(projectSize / 1024) + ' KB...' ); this.cloud.saveProject( - this.projectName, + name, projectBody, () => { this.recordSavedChanges(); @@ -6639,7 +6628,7 @@ IDE_Morph.prototype.exportProjectMedia = function (name) { menu = this.showMessage('Exporting'); this.serializer.serialize(new Project(this.scenes, this.scene)); media = this.serializer.mediaXML(name); - this.saveXMLAs(media, this.projectName + ' media'); + this.saveXMLAs(media, this.getProjectName() + ' media'); menu.destroy(); this.showMessage('Exported!', 1); } catch (err) { @@ -6660,14 +6649,14 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { var menu, str; this.serializer.isCollectingMedia = true; if (name) { - this.setProjectName(name); + name = this.setProjectName(name); if (Process.prototype.isCatchingErrors) { try { menu = this.showMessage('Exporting'); str = this.serializer.serialize( new Project(this.scenes, this.scene) ); - this.saveXMLAs(str, this.projectName); + this.saveXMLAs(str, name); menu.destroy(); this.showMessage('Exported!', 1); } catch (err) { @@ -6679,7 +6668,7 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) { str = this.serializer.serialize( new Project(this.scenes, this.scene) ); - this.saveXMLAs(str, this.projectName); + this.saveXMLAs(str, name); menu.destroy(); this.showMessage('Exported!', 1); } @@ -6692,7 +6681,7 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { var menu, str, media, dta; this.serializer.isCollectingMedia = true; if (name) { - this.setProjectName(name); + name = this.setProjectName(name); if (Process.prototype.isCatchingErrors) { try { menu = this.showMessage('Exporting'); @@ -6701,7 +6690,7 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { ); media = this.serializer.mediaXML(name); dta = '' + str + media + ''; - this.saveXMLAs(dta, this.projectName); + this.saveXMLAs(dta, name); menu.destroy(); this.showMessage('Exported!', 1); } catch (err) { @@ -6715,7 +6704,7 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) { ); media = this.serializer.mediaXML(name); dta = '' + str + media + ''; - this.saveXMLAs(str, this.projectName); + this.saveXMLAs(str, name); menu.destroy(); this.showMessage('Exported!', 1); } @@ -7078,7 +7067,7 @@ ProjectDialogMorph.prototype.buildContents = function () { this.body.add(this.srcBar); if (this.task === 'save') { - this.nameField = new InputFieldMorph(this.ide.projectName); + this.nameField = new InputFieldMorph(this.ide.getProjectName()); this.body.add(this.nameField); } @@ -7789,7 +7778,7 @@ ProjectDialogMorph.prototype.saveProject = function () { this.saveCloudProject(); } } else if (this.source === 'disk') { - this.ide.exportProject(name, false); + this.ide.exportProject(name); this.ide.source = 'disk'; this.destroy(); } @@ -7876,7 +7865,7 @@ ProjectDialogMorph.prototype.shareProject = function () { this.ide.showMessage('shared.', 2); // Set the Shared URL if the project is currently open - if (proj.projectname === ide.projectName) { + if (proj.projectname === ide.getProjectName()) { var usr = ide.cloud.username, projectId = 'Username=' + encodeURIComponent(usr.toLowerCase()) + @@ -7920,7 +7909,7 @@ ProjectDialogMorph.prototype.unshareProject = function () { this.buttons.fixLayout(); this.rerender(); this.ide.showMessage('unshared.', 2); - if (proj.projectname === ide.projectName) { + if (proj.projectname === ide.getProjectName()) { location.hash = ''; } }, @@ -7960,7 +7949,7 @@ ProjectDialogMorph.prototype.publishProject = function () { this.ide.showMessage('published.', 2); // Set the Shared URL if the project is currently open - if (proj.projectname === ide.projectName) { + if (proj.projectname === ide.getProjectName()) { var usr = ide.cloud.username, projectId = 'Username=' + encodeURIComponent(usr.toLowerCase()) + @@ -10389,7 +10378,10 @@ SceneIconMorph.prototype.renameScene = function () { scene ); scene.stage.version = Date.now(); // +++ also do this in other places - ide.recordUnsavedChanges(); + if (scene === ide.scene) { + ide.controlBar.updateLabel(); + } + ide.recordUnsavedChanges(); // ++++ sceneify unsaved changes } } ).prompt( From 5503ce568d5016bfa0311980ed257464f0d61a98 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 15:31:23 +0200 Subject: [PATCH 66/92] sceneified project notes --- HISTORY.md | 1 + src/gui.js | 56 ++++++++++++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 9179c663..6ea7b739 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,6 +11,7 @@ ### 2021-05-21 * gui, scenes, store: proxied thumbnail, name and notes in project, restored in XML * gui: distinguished project name from scene names, removed hidden "export as plain text" option +* gui: sceneified project notes ### 2021-05-20 * gui: marked projectName to be refactored and sceneified diff --git a/src/gui.js b/src/gui.js index bc528c6a..719c8bd9 100644 --- a/src/gui.js +++ b/src/gui.js @@ -248,7 +248,6 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.globalVariables = this.scene.globalVariables; this.currentSprite = this.scene.addDefaultSprite(); this.sprites = this.scene.sprites; - this.projectNotes = this.scene.notes; // +++ sceneify rename to scene notes this.currentCategory = 'motion'; this.currentTab = 'scripts'; @@ -2112,7 +2111,7 @@ IDE_Morph.prototype.fixLayout = function (situation) { } }; -// IDE_Morph project name +// IDE_Morph project properties IDE_Morph.prototype.getProjectName = function () { return this.scenes.at(1).name; @@ -2124,7 +2123,7 @@ IDE_Morph.prototype.setProjectName = function (string) { if (name !== projectScene.name) { projectScene.name = name; projectScene.stage.version = Date.now(); - this.hasChangedMedia = true; // +++ sceneify this + this.recordUnsavedChanges(); // +++ sceneify this if (projectScene === this.scene) { this.controlBar.updateLabel(); } @@ -2132,6 +2131,22 @@ IDE_Morph.prototype.setProjectName = function (string) { return name; }; +IDE_Morph.prototype.getProjectNotes = function () { + return this.scenes.at(1).notes; +}; + +IDE_Morph.prototype.setProjectNotes = function (string) { + var projectScene = this.scenes.at(1); + if (string !== projectScene.notes) { + projectScene.notes = string; + projectScene.stage.version = Date.now(); + this.recordUnsavedChanges(); // +++ sceneify this + if (projectScene === this.scene) { + this.controlBar.updateLabel(); + } + } +}; + // IDE_Morph resizing IDE_Morph.prototype.setExtent = function (point) { @@ -3969,10 +3984,7 @@ IDE_Morph.prototype.projectMenu = function () { backup = this.availableBackup(shiftClicked); menu = new MenuMorph(this); - if (this.scenes.length() > 1) { - menu.addItem('Scenes...', 'scenesMenu'); - } - menu.addItem('Project notes...', 'editProjectNotes'); + menu.addItem('Notes...', 'editNotes'); menu.addLine(); menu.addPair('New', 'createNewProject', '^N'); menu.addPair('Open...', 'openProjectsBrowser', '^O'); @@ -4056,12 +4068,12 @@ IDE_Morph.prototype.projectMenu = function () { ); } - // +++ menu.addLine(); + if (this.scenes.length() > 1) { + menu.addItem('Scenes...', 'scenesMenu'); + } menu.addPair('New scene', 'createNewScene'); menu.addPair('Add scene...', 'addScene'); - // +++ - menu.addLine(); menu.addItem( 'Libraries...', @@ -4643,10 +4655,10 @@ IDE_Morph.prototype.scenesMenu = function () { menu.popup(world, pos); }; -IDE_Morph.prototype.editProjectNotes = function () { - var dialog = new DialogBoxMorph().withKey('projectNotes'), +IDE_Morph.prototype.editNotes = function () { + var dialog = new DialogBoxMorph().withKey('notes'), frame = new ScrollFrameMorph(), - text = new TextMorph(this.projectNotes || ''), + text = new TextMorph(this.scene.notes || ''), size = 250, world = this.world(); @@ -4676,13 +4688,13 @@ IDE_Morph.prototype.editProjectNotes = function () { dialog.target = this; dialog.action = (note) => { - this.projectNotes = note; - this.recordUnsavedChanges(); + this.scene.notes = note; + this.recordUnsavedChanges(); // +++ sceneify this }; dialog.justDropped = () => text.edit(); - dialog.labelString = 'Project Notes'; + dialog.labelString = 'Notes'; dialog.createLabel(); dialog.addBody(frame); dialog.addButton('ok', 'OK'); @@ -5049,7 +5061,7 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { // ++++ s } // project notes - notes = Process.prototype.reportTextSplit(this.projectNotes, 'line'); + notes = Process.prototype.reportTextSplit(this.scene.notes, 'line'); notes.asArray().forEach(paragraph => add(paragraph)); // table of contents @@ -5463,8 +5475,6 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { ); this.scene.captureGlobalSettings(); this.scene = scene; - // +++ this.projectName = scene.name; // remove - this.projectNotes = scene.notes || ''; this.globalVariables = scene.globalVariables; this.stage.destroy(); this.add(scene.stage); @@ -7107,7 +7117,7 @@ ProjectDialogMorph.prototype.buildContents = function () { this.body.add(this.preview); if (this.task === 'save') { - thumbnail = this.ide.stage.thumbnail( + thumbnail = this.ide.stage.thumbnail( // +++ get the project thumbnail SnapSerializer.prototype.thumbnailSize ); this.preview.texture = null; @@ -7131,7 +7141,7 @@ ProjectDialogMorph.prototype.buildContents = function () { if (this.task === 'open' || this.task === 'add') { this.notesText = new TextMorph(''); } else { // 'save' - this.notesText = new TextMorph(this.ide.projectNotes); + this.notesText = new TextMorph(this.ide.getProjectNotes()); this.notesText.isEditable = true; this.notesText.enableSelecting(); } @@ -7754,8 +7764,8 @@ ProjectDialogMorph.prototype.saveProject = function () { var name = this.nameField.contents().text.text, notes = this.notesText.text; - if (this.ide.projectNotes !== notes) { - this.ide.projectNotes = notes; + if (this.ide.getProjectNotes() !== notes) { + this.ide.setProjectNotes(notes); } if (name) { if (this.source === 'cloud') { From 0460a4061ffbf8a7addb3e424e0095093d419de4 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 15:37:03 +0200 Subject: [PATCH 67/92] adjusted project thumbnail in "save" dialog --- HISTORY.md | 1 + src/gui.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 6ea7b739..c0bd7579 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,6 +12,7 @@ * gui, scenes, store: proxied thumbnail, name and notes in project, restored in XML * gui: distinguished project name from scene names, removed hidden "export as plain text" option * gui: sceneified project notes +* gui: adjusted project thumbnail in "save" dialog ### 2021-05-20 * gui: marked projectName to be refactored and sceneified diff --git a/src/gui.js b/src/gui.js index 719c8bd9..888190cd 100644 --- a/src/gui.js +++ b/src/gui.js @@ -7117,7 +7117,7 @@ ProjectDialogMorph.prototype.buildContents = function () { this.body.add(this.preview); if (this.task === 'save') { - thumbnail = this.ide.stage.thumbnail( // +++ get the project thumbnail + thumbnail = this.ide.scenes.at(1).stage.thumbnail( SnapSerializer.prototype.thumbnailSize ); this.preview.texture = null; From 2846d899e30e006065c264c63cb3d95699a60fd3 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 15:51:14 +0200 Subject: [PATCH 68/92] some cleanups --- HISTORY.md | 1 + src/gui.js | 37 ++++++++++++++++--------------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c0bd7579..39cdce6b 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -13,6 +13,7 @@ * gui: distinguished project name from scene names, removed hidden "export as plain text" option * gui: sceneified project notes * gui: adjusted project thumbnail in "save" dialog +* gui: some cleanups ### 2021-05-20 * gui: marked projectName to be refactored and sceneified diff --git a/src/gui.js b/src/gui.js index 888190cd..c7f97a7e 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1922,17 +1922,17 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { this.corral.frame = frame; this.corral.add(frame); - // scenes +++ + // scenes corral this.corral.album = keepSceneAlbum ? album : new SceneAlbumMorph(this, this.sliderColor); - this.corral.album.color = this.frameColor; // this.groupColor; + this.corral.album.color = this.frameColor; this.corral.add(this.corral.album); this.corral.fixLayout = function () { this.stageIcon.setCenter(this.center()); this.stageIcon.setLeft(this.left() + padding); - // scenes +++ + // scenes if (myself.scenes.length() < 2) { this.album.hide(); } else { @@ -1941,7 +1941,9 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { this.album.setLeft(this.left()); this.album.setTop(this.stageIcon.bottom() + padding); this.album.setWidth(this.stageIcon.width() + padding * 2); - this.album.setHeight(this.height() - this.stageIcon.height() - padding); // +++ + this.album.setHeight( + this.height() - this.stageIcon.height() - padding + ); } this.frame.setLeft(this.stageIcon.right() + padding); @@ -4716,7 +4718,7 @@ IDE_Morph.prototype.newProject = function () { this.openProject(project); }; -IDE_Morph.prototype.createNewScene = function () { // +++ +IDE_Morph.prototype.createNewScene = function () { var setting = this.isAddingScenes; this.isAddingScenes = true; this.newProject(); @@ -4900,7 +4902,7 @@ IDE_Morph.prototype.exportScriptsPicture = function () { this.saveCanvasAs(pic, this.scene.name || localize('Untitled')); }; -IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { // ++++ sceneify: make it "scene summary" and include it in the scene icon context menu +IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { var html, head, meta, css, body, pname, notes, toc, globalVars, stage = this.stage; @@ -5000,7 +5002,7 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { // ++++ s } } - pname = this.getProjectName() || localize('untitled'); // ++++ sceneify + pname = this.scene.name || localize('untitled'); html = new XML_Element('html'); html.attributes.lang = SnapTranslator.language; @@ -5181,7 +5183,7 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) { // ++++ s IDE_Morph.prototype.openProjectString = function (str, callback) { var msg; - if (this.bulkDropInProgress || this.isAddingScenes) { // +++ + if (this.bulkDropInProgress || this.isAddingScenes) { this.rawOpenProjectString(str); if (callback) {callback(); } return; @@ -5229,7 +5231,7 @@ IDE_Morph.prototype.openCloudDataString = function (str) { IDE_Morph.prototype.rawOpenCloudDataString = function (str) { var model, - setting = this.isAddingScenes; // +++ + setting = this.isAddingScenes; if (this.isAddingNextScene) { this.isAddingScenes = true; @@ -5948,7 +5950,7 @@ IDE_Morph.prototype.createNewProject = function () { this.backup(() => this.newProject()); }; -IDE_Morph.prototype.addScene = function () { // +++ +IDE_Morph.prototype.addScene = function () { var setting = this.isAddingScenes; if (location.protocol === 'file:') { // bypass the project import dialog and directly pop up @@ -7678,7 +7680,7 @@ ProjectDialogMorph.prototype.recoveryDialog = function () { new ProjectRecoveryDialogMorph(this.ide, proj.projectname, this).popUp(); }; -ProjectDialogMorph.prototype.addScene = function () { // +++ +ProjectDialogMorph.prototype.addScene = function () { var proj = this.listField.selected, src; if (!proj) {return; } @@ -10233,7 +10235,7 @@ SceneIconMorph.uber = ToggleButtonMorph.prototype; // SceneIconMorph settings -SceneIconMorph.prototype.thumbSize = new Point(40, 30); // +++ (40, 40), (80, 60); +SceneIconMorph.prototype.thumbSize = new Point(40, 30); SceneIconMorph.prototype.labelShadowOffset = null; SceneIconMorph.prototype.labelShadowColor = null; SceneIconMorph.prototype.labelColor = WHITE; @@ -10282,7 +10284,7 @@ SceneIconMorph.prototype.init = function (aScene) { colors, // color overrides, : [normal, highlight, pressed] null, // target - not needed here action, // a toggle function - this.object.name || localize('untitled'), // label string // +++ + this.object.name || localize('untitled'), // label string query, // predicate/selector null, // environment null // hint @@ -10312,12 +10314,7 @@ SceneIconMorph.prototype.createThumbnail = function () { this.add(this.thumbnail); }; -/* +++ -SceneIconMorph.prototype.createLabel - = SpriteIconMorph.prototype.createLabel; -*/ - -SceneIconMorph.prototype.createLabel = function () { // +++ +SceneIconMorph.prototype.createLabel = function () { var txt; if (this.label) { @@ -10452,8 +10449,6 @@ SceneIconMorph.prototype.render // SceneIconMorph drag & drop SceneIconMorph.prototype.rootForGrab = function () { - // +++ to do: make sure scene icons can only be grabbed if there are - // more than 1 return this; }; From 7caa5f04c61d62efe7a697f98133e7c79c0f3850 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 16:54:41 +0200 Subject: [PATCH 69/92] sceneified unsaved changes management --- HISTORY.md | 1 + src/gui.js | 17 +++++++++-------- src/scenes.js | 1 + 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 39cdce6b..b6f41925 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -14,6 +14,7 @@ * gui: sceneified project notes * gui: adjusted project thumbnail in "save" dialog * gui: some cleanups +* gui, scenes: sceneified unsaved changes management ### 2021-05-20 * gui: marked projectName to be refactored and sceneified diff --git a/src/gui.js b/src/gui.js index c7f97a7e..76986d40 100644 --- a/src/gui.js +++ b/src/gui.js @@ -277,9 +277,7 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.filePicker = null; // incrementally saving projects to the cloud is currently unused - this.hasChangedMedia = false; - - this.hasUnsavedEdits = false; // keeping track of when to internally backup + this.hasChangedMedia = false; // +++ sceneify, or get of it this.isAnimating = true; this.paletteWidth = 200; // initially same as logo width @@ -1175,7 +1173,7 @@ IDE_Morph.prototype.createControlBar = function () { }; this.controlBar.updateLabel = function () { - var prefix = myself.hasUnsavedEdits ? '\u270E ' : '', + var prefix = myself.hasUnsavedEdits() ? '\u270E ' : '', suffix = myself.world().isDevMode ? ' - ' + localize('development mode') : '', txt; @@ -2796,13 +2794,17 @@ IDE_Morph.prototype.hasLocalStorage = function () { // IDE_Morph recording unsaved changes +IDE_Morph.prototype.hasUnsavedEdits = function () { + return this.scenes.itemsArray().some(any => any.hasUnsavedEdits); +}; + IDE_Morph.prototype.recordUnsavedChanges = function () { - this.hasUnsavedEdits = true; + this.scene.hasUnsavedEdits = true; this.updateChanges(); }; IDE_Morph.prototype.recordSavedChanges = function () { - this.hasUnsavedEdits = false; + this.scenes.itemsArray().forEach(scene => scene.hasUnsavedEdits = false); this.updateChanges(); }; @@ -2826,7 +2828,7 @@ IDE_Morph.prototype.backup = function (callback) { // Save the current project for the currently logged in user // to localstorage, then perform the given callback, e.g. // load a new project. - if (this.hasUnsavedEdits) { + if (this.hasUnsavedEdits()) { this.confirm( 'Replace the current project with a new one?', 'Unsaved Changes!', @@ -5493,7 +5495,6 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { } }); scene.applyGlobalSettings(); - this.hasUnsavedEdits = false; this.world().keyboardFocus = this.stage; }; diff --git a/src/scenes.js b/src/scenes.js index 52e9a8d6..83fc8b9c 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -116,6 +116,7 @@ function Scene(aStageMorph) { this.globalVariables = aStageMorph ? aStageMorph.globalVariables() : new VariableFrame(); this.stage = aStageMorph || new StageMorph(this.globalVariables); + this.hasUnsavedEdits = false; // cached IDE state this.sprites = new List(); From 0c8baa9f7fef29948a048fe6e0c72a77f295adf8 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 17:03:45 +0200 Subject: [PATCH 70/92] corrected a typo-glitch --- src/gui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui.js b/src/gui.js index 76986d40..c5bcfdd0 100644 --- a/src/gui.js +++ b/src/gui.js @@ -4020,7 +4020,7 @@ IDE_Morph.prototype.projectMenu = function () { 'Export project...', () => { var pn = this.getProjectName(); - if (this.pn) { + if (pn) { this.exportProject(pn); } else { this.prompt( From e4ca7f2a21d5b3949974ce182200c9ef4bbb0254 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 21 May 2021 23:34:09 +0200 Subject: [PATCH 71/92] fixed search-blocks for scenesMenu --- HISTORY.md | 1 + snap.html | 2 +- src/blocks.js | 22 ++++++++++++---------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b6f41925..f091c02e 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -15,6 +15,7 @@ * gui: adjusted project thumbnail in "save" dialog * gui: some cleanups * gui, scenes: sceneified unsaved changes management +* blocks: fixed search-blocks for scenesMenu ### 2021-05-20 * gui: marked projectName to be refactored and sceneified diff --git a/snap.html b/snap.html index aab7a08b..bfa09d78 100755 --- a/snap.html +++ b/snap.html @@ -8,7 +8,7 @@ - + diff --git a/src/blocks.js b/src/blocks.js index 2e035ecc..1bb60c60 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -158,7 +158,7 @@ CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2021-April-12'; +modules.blocks = '2021-May-21'; var SyntaxElementMorph; var BlockMorph; @@ -9745,15 +9745,17 @@ InputSlotMorph.prototype.audioMenu = function (searching) { }; InputSlotMorph.prototype.scenesMenu = function (searching) { - var scenes = this.parentThatIsA(IDE_Morph).scenes, - dict = {}; - - if (!searching && scenes.length() > 1) { - scenes.itemsArray().forEach(scn => { - if (scn.name) { - dict[scn.name] = scn.name; - } - }); + var dict = {}, + scenes; + if (!searching) { + scenes = this.parentThatIsA(IDE_Morph).scenes; + if (scenes.length() > 1) { + scenes.itemsArray().forEach(scn => { + if (scn.name) { + dict[scn.name] = scn.name; + } + }); + } } dict['~'] = null; dict.next = ['next']; From 132fc0257ded5f87daa0051573dfa6f9f22b27bd Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 25 May 2021 09:22:50 -1000 Subject: [PATCH 72/92] Unified Palette: Clicking Category scrolls palette to the right section --- src/blocks.js | 22 +++++++++---------- src/gui.js | 60 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/blocks.js b/src/blocks.js index 1bb60c60..2e035ecc 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -158,7 +158,7 @@ CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2021-May-21'; +modules.blocks = '2021-April-12'; var SyntaxElementMorph; var BlockMorph; @@ -9745,17 +9745,15 @@ InputSlotMorph.prototype.audioMenu = function (searching) { }; InputSlotMorph.prototype.scenesMenu = function (searching) { - var dict = {}, - scenes; - if (!searching) { - scenes = this.parentThatIsA(IDE_Morph).scenes; - if (scenes.length() > 1) { - scenes.itemsArray().forEach(scn => { - if (scn.name) { - dict[scn.name] = scn.name; - } - }); - } + var scenes = this.parentThatIsA(IDE_Morph).scenes, + dict = {}; + + if (!searching && scenes.length() > 1) { + scenes.itemsArray().forEach(scn => { + if (scn.name) { + dict[scn.name] = scn.name; + } + }); } dict['~'] = null; dict.next = ['next']; diff --git a/src/gui.js b/src/gui.js index f0d69a08..f842c891 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1218,7 +1218,8 @@ IDE_Morph.prototype.createControlBar = function () { }; IDE_Morph.prototype.createCategories = function () { - var myself = this; + var myself = this, + categorySelectionAction; if (this.categories) { this.categories.destroy(); @@ -1228,17 +1229,27 @@ IDE_Morph.prototype.createCategories = function () { this.categories.bounds.setWidth(this.paletteWidth); // this.categories.getRenderColor = ScriptsMorph.prototype.getRenderColor; + if (this.scene.unifiedPalette) { - // TODO: What should this look like? - // Ensure there's a min height so the Sprite toolbar has a correct layout. - // Include the search bar + make a block button here? - this.categories.bounds.setHeight(84); - let wastedSpace = new Morph(); - wastedSpace.setExtent(new Point(this.paletteWidth, 84)); - this.categories.add(wastedSpace); - this.add(this.categories); - return; + categorySelectionAction = scrollToCategory; + } else { + categorySelectionAction = changePallete; } + + function changePallete(category) { + return () => { + myself.currentCategory = category; + myself.categories.children.forEach(each => + each.refresh() + ); + myself.refreshPalette(true); + } + } + + function scrollToCategory(category) { + return () => myself.scrollPaletteToCategory(category); + } + function addCategoryButton(category) { var labelWidth = 75, colors = [ @@ -1251,13 +1262,7 @@ IDE_Morph.prototype.createCategories = function () { button = new ToggleButtonMorph( colors, myself, // the IDE is the target - () => { - myself.currentCategory = category; - myself.categories.children.forEach(each => - each.refresh() - ); - myself.refreshPalette(true); - }, + categorySelectionAction(category), category[0].toUpperCase().concat(category.slice(1)), // label () => myself.currentCategory === category, // query null, // env @@ -1324,13 +1329,11 @@ IDE_Morph.prototype.createCategories = function () { IDE_Morph.prototype.createPalette = function (forSearching) { // assumes that the logo pane has already been created // needs the categories pane for layout - let unifiedPalette = this.scene.unifiedPalette; if (this.palette) { this.palette.destroy(); } - // TODO: Always show if unified? if (forSearching) { this.palette = new ScrollFrameMorph( null, @@ -2501,6 +2504,21 @@ IDE_Morph.prototype.refreshPalette = function (shouldIgnorePosition) { } }; +IDE_Morph.prototype.scrollPaletteToCategory = function (category) { + var topOfCategory, + palette = this.palette.contents; + // pallete scroll top - block top + palette actual top + palette padding + palette.children.some(block => { + if (block.category == category) { + topOfCategory = block; + return true; + } + }); + palette.setTop( + palette.top() - topOfCategory.top() + this.palette.top() + this.palette.padding + ); +} + IDE_Morph.prototype.pressStart = function () { if (this.world().currentKey === 16) { // shiftClicked this.toggleFastTracking(); @@ -3992,8 +4010,8 @@ IDE_Morph.prototype.settingsMenu = function () { this.refreshPalette(true); }, this.scene.unifiedPalette, - 'uncheck to disable\nusing operators on lists and tables', - 'check to enable\nusing operators on lists and tables', + 'uncheck to show only the selected category\'s blocks', + 'check to show all blocks in a single palette', false ); addPreference( From a8393be6b05f156e9d333e8b7f07f858f39b5dca Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Thu, 3 Jun 2021 07:35:55 -1000 Subject: [PATCH 73/92] Add support for saving and loading unified palette --- snap.html | 8 ++++---- src/store.js | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/snap.html b/snap.html index bfa09d78..6e55365a 100755 --- a/snap.html +++ b/snap.html @@ -8,11 +8,11 @@ - + - - + + @@ -21,7 +21,7 @@ - + diff --git a/src/store.js b/src/store.js index 80438983..09b115c3 100644 --- a/src/store.js +++ b/src/store.js @@ -381,6 +381,8 @@ SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { } model.globalVariables = model.scene.childNamed('variables'); + scene.unifiedPalette = model.attributes.unifiedPalette === 'true'; + /* Stage */ model.stage = model.scene.require('stage'); @@ -453,8 +455,6 @@ SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { scene.enableSublistIDs = model.stage.attributes.sublistIDs === 'true'; - scene.unifiedPalette = model.stage.unifiedPalette === 'true'; - model.hiddenPrimitives = model.scene.childNamed('hidden'); if (model.hiddenPrimitives) { model.hiddenPrimitives.contents.split(' ').forEach( @@ -1693,7 +1693,7 @@ Scene.prototype.toXML = function (serializer) { tmp.captureGlobalSettings(); this.applyGlobalSettings(); xml = serializer.format( - '' + + '' + '$' + '$' + '%' + @@ -1703,6 +1703,7 @@ Scene.prototype.toXML = function (serializer) { '%' + // stage '', this.name || localize('Untitled'), + this.unifiedPalette, this.notes || '', Object.keys(StageMorph.prototype.hiddenPrimitives).reduce( (a, b) => a + ' ' + b, From b3f758243f478d0ba7f26d698fabf12f40a51791 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Fri, 4 Jun 2021 12:18:29 -1000 Subject: [PATCH 74/92] unified palette is reflected when switching/loading scenes --- src/gui.js | 11 ++++++++++- src/store.js | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/gui.js b/src/gui.js index f842c891..b4ac9a4c 100644 --- a/src/gui.js +++ b/src/gui.js @@ -5544,7 +5544,6 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { this.stage.pauseGenericHatBlocks(); this.createCorral(!refreshAlbum); // keep scenes this.selectSprite(this.scene.currentSprite); - this.fixLayout(); this.corral.album.updateSelection(); this.corral.album.contents.children.forEach(function (morph) { if (morph.state) { @@ -5553,6 +5552,16 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { }); scene.applyGlobalSettings(); this.world().keyboardFocus = this.stage; + + if (scene.unifiedPalette) { + this.currentCategory = 'unified'; + } else if (this.currentCategory == 'unified' && !scene.unifiedPalette) { + // Only switch to motion if the palette is no longer unified. + this.currentCategory = 'motion'; + } + + this.fixLayout(); + this.refreshPalette(); }; IDE_Morph.prototype.setURL = function (str) { diff --git a/src/store.js b/src/store.js index 09b115c3..4140d23d 100644 --- a/src/store.js +++ b/src/store.js @@ -381,7 +381,7 @@ SnapSerializer.prototype.loadScene = function (xmlNode, remixID) { } model.globalVariables = model.scene.childNamed('variables'); - scene.unifiedPalette = model.attributes.unifiedPalette === 'true'; + scene.unifiedPalette = model.scene.attributes.unifiedPalette === 'true'; /* Stage */ From 3026bcea3534b1d52ae31d4edd4aa2d999651cf7 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Fri, 4 Jun 2021 13:18:12 -1000 Subject: [PATCH 75/92] Refactor loading unified palette --- src/gui.js | 40 ++++++++++++++++++++-------------------- src/scenes.js | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/gui.js b/src/gui.js index b4ac9a4c..2677165d 100644 --- a/src/gui.js +++ b/src/gui.js @@ -248,11 +248,7 @@ IDE_Morph.prototype.init = function (isAutoFill) { this.globalVariables = this.scene.globalVariables; this.currentSprite = this.scene.addDefaultSprite(); this.sprites = this.scene.sprites; - if (this.scene.unifiedPalette) { - this.currentCategory = 'unified'; - } else { - this.currentCategory = 'motion'; - } + this.currentCategory = 'motion'; this.currentTab = 'scripts'; // logoURL is disabled because the image data is hard-copied @@ -2022,6 +2018,20 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { }; }; +IDE_Morph.prototype.unsetUnifiedPalete = function () { + this.scene.unifiedPalette = false; + this.currentCategory = 'motion'; + this.createCategories(); + this.refreshPalette(); +} + +IDE_Morph.prototype.setUnifiedPalete = function () { + this.scene.unifiedPalette = true; + this.currentCategory = 'unified'; + this.createCategories(); + this.refreshPalette(); +} + // IDE_Morph layout IDE_Morph.prototype.fixLayout = function (situation) { @@ -3995,19 +4005,11 @@ IDE_Morph.prototype.settingsMenu = function () { addPreference( 'Unified Palette', () => { - this.scene.unifiedPalette = !this.scene.unifiedPalette; if (this.scene.unifiedPalette) { - this.currentCategory = 'unified'; + this.unsetUnifiedPalete(); } else { - this.currentCategory = 'motion'; + this.setUnifiedPalete(); } - this.createCategories(); - this.categories.fixLayout(); - this.fixLayout(); - this.flushBlocksCache(); - this.flushPaletteCache(); - this.currentSprite.palette(this.currentCategory); - this.refreshPalette(true); }, this.scene.unifiedPalette, 'uncheck to show only the selected category\'s blocks', @@ -5553,15 +5555,13 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { scene.applyGlobalSettings(); this.world().keyboardFocus = this.stage; - if (scene.unifiedPalette) { - this.currentCategory = 'unified'; + if (this.currentCategory != 'unified' && scene.unifiedPalette) { + this.setUnifiedPalete(); } else if (this.currentCategory == 'unified' && !scene.unifiedPalette) { - // Only switch to motion if the palette is no longer unified. - this.currentCategory = 'motion'; + this.unsetUnifiedPalete(); } this.fixLayout(); - this.refreshPalette(); }; IDE_Morph.prototype.setURL = function (str) { diff --git a/src/scenes.js b/src/scenes.js index e797141b..95d38205 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -117,6 +117,7 @@ function Scene(aStageMorph) { aStageMorph.globalVariables() : new VariableFrame(); this.stage = aStageMorph || new StageMorph(this.globalVariables); this.hasUnsavedEdits = false; + this.unifiedPalette = false; // cached IDE state this.sprites = new List(); @@ -126,7 +127,6 @@ function Scene(aStageMorph) { this.hiddenPrimitives = {}; this.codeMappings = {}; this.codeHeaders = {}; - this.unifiedPalette = false; // global settings (copied) this.enableCodeMapping = false; From 6f8d3fa327d2e98ea61d5ec2c0fc4cfd0fee50fb Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Mon, 7 Jun 2021 22:18:27 -1000 Subject: [PATCH 76/92] Refactor turning unified palette on and off --- src/gui.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/gui.js b/src/gui.js index 2677165d..c773812e 100644 --- a/src/gui.js +++ b/src/gui.js @@ -2022,14 +2022,24 @@ IDE_Morph.prototype.unsetUnifiedPalete = function () { this.scene.unifiedPalette = false; this.currentCategory = 'motion'; this.createCategories(); - this.refreshPalette(); + this.categories.fixLayout(); + this.fixLayout(); + this.flushBlocksCache(); + this.flushPaletteCache(); + this.currentSprite.palette(this.currentCategory); + this.refreshPalette(true); } IDE_Morph.prototype.setUnifiedPalete = function () { this.scene.unifiedPalette = true; this.currentCategory = 'unified'; this.createCategories(); - this.refreshPalette(); + this.categories.fixLayout(); + this.fixLayout(); + this.flushBlocksCache(); + this.flushPaletteCache(); + this.currentSprite.palette(this.currentCategory); + this.refreshPalette(true); } // IDE_Morph layout From 48e43a30c5ddab91b61baded8f1bef08d05d20e8 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Mon, 7 Jun 2021 22:27:41 -1000 Subject: [PATCH 77/92] simplify hiding primitives function --- src/blocks.js | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/blocks.js b/src/blocks.js index 2e035ecc..85fe00d5 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -926,7 +926,7 @@ SyntaxElementMorph.prototype.labelParts = { type: 'ring slot' tags: 'static', kind: 'command', 'reporter', 'predicate' - + */ '%rc': { type: 'ring slot', @@ -2052,7 +2052,7 @@ SyntaxElementMorph.prototype.fixLayout = function () { return; } } - + this.fixHighlight(); }; @@ -3233,21 +3233,10 @@ BlockMorph.prototype.developersMenu = function () { }; BlockMorph.prototype.hidePrimitive = function () { - var ide = this.parentThatIsA(IDE_Morph), - dict, - cat; + var ide = this.parentThatIsA(IDE_Morph); if (!ide) {return; } StageMorph.prototype.hiddenPrimitives[this.selector] = true; - dict = { - doWarp: 'control', - reifyScript: 'operators', - reifyReporter: 'operators', - reifyPredicate: 'operators', - doDeclareVariables: 'variables' - }; - cat = dict[this.selector] || this.category; - if (cat === 'lists') {cat = 'variables'; } - ide.flushBlocksCache(cat); + ide.flushBlocksCache(ide.currentCategory); ide.refreshPalette(); }; @@ -4247,7 +4236,7 @@ BlockMorph.prototype.render = function (ctx) { this.outlinePath(ctx, 0); ctx.closePath(); ctx.fill(); - + // add 3D-Effect: this.drawEdges(ctx); } @@ -5883,11 +5872,11 @@ function ReporterBlockMorph(isPredicate) { ReporterBlockMorph.prototype.init = function (isPredicate) { ReporterBlockMorph.uber.init.call(this); this.isPredicate = isPredicate || false; - + this.bounds.setExtent(new Point(50, 22).multiplyBy(this.scale)); this.fixLayout(); this.rerender(); - + this.cachedSlotSpec = null; // don't serialize this.isLocalVarTemplate = null; // don't serialize }; @@ -6578,7 +6567,7 @@ RingMorph.prototype.render = function (ctx) { // ctx.closePath(); ctx.clip('evenodd'); ctx.fillRect(0, 0, this.width(), this.height()); - + // add 3D-Effect: this.drawEdges(ctx); } @@ -14263,7 +14252,7 @@ ScriptFocusMorph.prototype.reactToKeyEvent = function (key) { cmd = new CommandBlockMorph(); cmd.setSpec('command %cmdRing'); - + rings = new CommandBlockMorph(); rings.setSpec('reporter %repRing predicate %predRing'); From ccc9edf4fcb463367ec91c97140faa23989fa96a Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Mon, 7 Jun 2021 22:48:08 -1000 Subject: [PATCH 78/92] tidy up spacing in unified palette view --- src/objects.js | 53 +++++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/objects.js b/src/objects.js index e1ab0588..5ada6a8e 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2385,6 +2385,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(watcherToggle('direction')); blocks.push(block('direction', this.inheritsAttribute('direction'))); + if (cat === 'unified') {blocks.push('='); } } if (cat === 'looks' || cat === 'unified') { @@ -2439,6 +2440,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doScreenshot')); } + if (cat === 'unified') {blocks.push('='); } } if (cat === 'sound' || cat === 'unified') { @@ -2486,6 +2488,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doPlayFrequency')); } + if (cat === 'unified') {blocks.push('='); } } if (cat === 'pen' || cat === 'unified') { @@ -2513,6 +2516,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doPasteOn')); blocks.push(block('doCutFrom')); + if (cat === 'unified') {blocks.push('='); } } if (cat === 'control' || cat === 'unified') { @@ -2562,6 +2566,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('doPauseAll')); + if (cat === 'unified') {blocks.push('='); } } if (cat === 'sensing' || cat === 'unified') { @@ -2625,6 +2630,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportYieldCount')); } + if (cat === 'unified') {blocks.push('='); } } if (cat === 'operators' || cat === 'unified') { @@ -2687,6 +2693,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportTextFunction')); } + if (cat === 'unified') {blocks.push('='); } } if (cat === 'variables' || cat === 'unified') { @@ -2830,12 +2837,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { } } - if (cat !== ' unified') { - // TODO: Should probably show thos. - // what should category default to? - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - } + blocks.push('='); + blocks.push(this.makeBlockButton(cat)); return blocks; }; @@ -8582,7 +8585,7 @@ StageMorph.prototype.blockTemplates = function (category) { } } - if (cat === 'motion' || cat === 'unified') { + if (cat === 'motion') { txt = new TextMorph(localize( 'Stage selected:\nno motion primitives' @@ -8590,9 +8593,8 @@ StageMorph.prototype.blockTemplates = function (category) { txt.fontSize = 9; txt.setColor(this.paletteTextColor); blocks.push(txt); - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); + if (cat === 'unified') {blocks.push('='); } } if (cat === 'looks' || cat === 'unified') { @@ -8633,10 +8635,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doScreenshot')); } - ///////////////////////////////// - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - + if (cat === 'unified') {blocks.push('='); } } if (cat === 'sound' || cat === 'unified') { @@ -8683,10 +8682,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doPlayFrequency')); } - ///////////////////////////////// - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - + if (cat === 'unified') {blocks.push('='); } } if (cat === 'pen' || cat === 'unified') { @@ -8700,9 +8696,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('doPasteOn')); blocks.push(block('doCutFrom')); - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); + if (cat === 'unified') {blocks.push('='); } } if (cat === 'control' || cat === 'unified') { @@ -8749,9 +8744,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('newClone')); blocks.push('-'); blocks.push(block('doPauseAll')); - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); + if (cat === 'unified') {blocks.push('='); } } if (cat === 'sensing' || cat === 'unified') { @@ -8810,11 +8804,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportYieldCount')); } - ///////////////////////////////// - - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - + if (cat === 'unified') {blocks.push('='); } } if (cat === 'operators' || cat === 'unified') { @@ -8877,10 +8867,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportTextFunction')); } - ////////////////////////////////// - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - + if (cat === 'unified') {blocks.push('='); } } if (cat === 'variables' || cat === 'unified') { @@ -9010,8 +8997,12 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('='); } - blocks.push(this.makeBlockButton()); + if (cat === 'unified') {blocks.push('='); } } + + blocks.push('='); + blocks.push(this.makeBlockButton(cat)); + return blocks; }; From 7ac61ea30b25ea402ea713be8d8b3267e44c5ee3 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Mon, 28 Jun 2021 21:58:10 -0700 Subject: [PATCH 79/92] Show custom blocks in the correct spot --- src/blocks.js | 2 +- src/gui.js | 5 +- src/objects.js | 970 +++++++++++++++++++++++-------------------------- 3 files changed, 463 insertions(+), 514 deletions(-) diff --git a/src/blocks.js b/src/blocks.js index 85fe00d5..5e6429cb 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -7184,7 +7184,7 @@ ScriptsMorph.prototype.userMenu = function () { } else { obj.customBlocks.push(definition); } - ide.flushPaletteCache(); + ide.flushBlocksCache(); ide.refreshPalette(); new BlockEditorMorph(definition, obj).popUp(); } diff --git a/src/gui.js b/src/gui.js index c773812e..d985df70 100644 --- a/src/gui.js +++ b/src/gui.js @@ -2025,7 +2025,6 @@ IDE_Morph.prototype.unsetUnifiedPalete = function () { this.categories.fixLayout(); this.fixLayout(); this.flushBlocksCache(); - this.flushPaletteCache(); this.currentSprite.palette(this.currentCategory); this.refreshPalette(true); } @@ -5729,12 +5728,16 @@ IDE_Morph.prototype.switchToDevMode = function () { }; IDE_Morph.prototype.flushBlocksCache = function (category) { + alert(`blocks cacbe flushed for ${category}`); // if no category is specified, the whole cache gets flushed + // the 'unified' category is always flushed. if (category) { this.stage.blocksCache[category] = null; + this.stage.blocksCache.unified = null; this.stage.children.forEach(m => { if (m instanceof SpriteMorph) { m.blocksCache[category] = null; + m.blocksCache.unified = null; } }); } else { diff --git a/src/objects.js b/src/objects.js index 5ada6a8e..8e48307c 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2097,7 +2097,7 @@ SpriteMorph.prototype.fixLayout = function () { this.setCenter(currentCenter, true); // just me this.rotationOffset = this.extent().divideBy(2); } - }; +}; SpriteMorph.prototype.render = function (ctx) { var myself = this, @@ -2275,8 +2275,7 @@ SpriteMorph.prototype.variableBlock = function (varName, isLocalTemplate) { SpriteMorph.prototype.blockTemplates = function (category) { var blocks = [], myself = this, varNames, button, - cat = category || 'motion', txt, - inheritedVars = this.inheritedVariableNames(); + cat = category || 'motion', paletteDefitions; function block(selector, isGhosted) { if (StageMorph.prototype.hiddenPrimitives[selector]) { @@ -2342,6 +2341,13 @@ SpriteMorph.prototype.blockTemplates = function (category) { return menu; } + function devModeText() { + let txt = new TextMorph(localize('development mode \ndebugging primitives:')); + txt.fontSize = 9; + txt.setColor(this.paletteTextColor); + return txt; + } + function addVar(pair) { var ide; if (pair) { @@ -2358,487 +2364,496 @@ SpriteMorph.prototype.blockTemplates = function (category) { } } - if (cat === 'motion' || cat === 'unified') { - - blocks.push(block('forward')); - blocks.push(block('turn')); - blocks.push(block('turnLeft')); - blocks.push('-'); - blocks.push(block('setHeading')); - blocks.push(block('doFaceTowards')); - blocks.push('-'); - blocks.push(block('gotoXY')); - blocks.push(block('doGotoObject')); - blocks.push(block('doGlide')); - blocks.push('-'); - blocks.push(block('changeXPosition')); - blocks.push(block('setXPosition')); - blocks.push(block('changeYPosition')); - blocks.push(block('setYPosition')); - blocks.push('-'); - blocks.push(block('bounceOffEdge')); - blocks.push('-'); - blocks.push(watcherToggle('xPosition')); - blocks.push(block('xPosition', this.inheritsAttribute('x position'))); - blocks.push(watcherToggle('yPosition')); - blocks.push(block('yPosition', this.inheritsAttribute('y position'))); - blocks.push(watcherToggle('direction')); - blocks.push(block('direction', this.inheritsAttribute('direction'))); - - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'looks' || cat === 'unified') { - - blocks.push(block('doSwitchToCostume')); - blocks.push(block('doWearNextCostume')); - blocks.push(watcherToggle('getCostumeIdx')); - blocks.push(block('getCostumeIdx', this.inheritsAttribute('costume #'))); - blocks.push('-'); - blocks.push(block('doSayFor')); - blocks.push(block('bubble')); - blocks.push(block('doThinkFor')); - blocks.push(block('doThink')); - blocks.push('-'); - blocks.push(block('reportGetImageAttribute')); - blocks.push(block('reportNewCostumeStretched')); - blocks.push(block('reportNewCostume')); - blocks.push('-'); - blocks.push(block('changeEffect')); - blocks.push(block('setEffect')); - blocks.push(block('clearEffects')); - blocks.push(block('getEffect')); - blocks.push('-'); - blocks.push(block('changeScale')); - blocks.push(block('setScale')); - blocks.push(watcherToggle('getScale')); - blocks.push(block('getScale', this.inheritsAttribute('size'))); - blocks.push('-'); - blocks.push(block('show')); - blocks.push(block('hide')); - blocks.push(watcherToggle('reportShown')); - blocks.push(block('reportShown', this.inheritsAttribute('shown?'))); - blocks.push('-'); - blocks.push(block('goToLayer')); - blocks.push(block('goBack')); - blocks.push('-'); - blocks.push(block('doSwitchToScene')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { + paletteDefitions = { + motion: () => { + blocks.push(block('forward')); + blocks.push(block('turn')); + blocks.push(block('turnLeft')); blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(block('setHeading')); + blocks.push(block('doFaceTowards')); blocks.push('-'); - blocks.push(block('log')); - blocks.push(block('alert')); + blocks.push(block('gotoXY')); + blocks.push(block('doGotoObject')); + blocks.push(block('doGlide')); blocks.push('-'); - blocks.push(block('doScreenshot')); - } - - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'sound' || cat === 'unified') { - - blocks.push(block('playSound')); - blocks.push(block('doPlaySoundUntilDone')); - blocks.push(block('doStopAllSounds')); - blocks.push('-'); - blocks.push(block('doPlaySoundAtRate')); - blocks.push(block('reportGetSoundAttribute')); - blocks.push(block('reportNewSoundFromSamples')); - blocks.push('-'); - blocks.push(block('doRest')); - blocks.push(block('doPlayNote')); - blocks.push(block('doSetInstrument')); - blocks.push('-'); - blocks.push(block('doChangeTempo')); - blocks.push(block('doSetTempo')); - blocks.push(watcherToggle('getTempo')); - blocks.push(block('getTempo')); - blocks.push('-'); - blocks.push(block('changeVolume')); - blocks.push(block('setVolume')); - blocks.push(watcherToggle('getVolume')); - blocks.push(block('getVolume', this.inheritsAttribute('volume'))); - blocks.push('-'); - blocks.push(block('changePan')); - blocks.push(block('setPan')); - blocks.push(watcherToggle('getPan')); - blocks.push(block('getPan', this.inheritsAttribute('balance'))); - blocks.push('-'); - blocks.push(block('playFreq')); - blocks.push(block('stopFreq')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { + blocks.push(block('changeXPosition')); + blocks.push(block('setXPosition')); + blocks.push(block('changeYPosition')); + blocks.push(block('setYPosition')); blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(block('bounceOffEdge')); blocks.push('-'); - blocks.push(block('doPlayFrequency')); - } - - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'pen' || cat === 'unified') { - - blocks.push(block('clear')); - blocks.push('-'); - blocks.push(block('down')); - blocks.push(block('up')); - blocks.push(watcherToggle('getPenDown')); - blocks.push(block('getPenDown', this.inheritsAttribute('pen down?'))); - blocks.push('-'); - blocks.push(block('setColor')); - blocks.push(block('changePenHSVA')); - blocks.push(block('setPenHSVA')); - blocks.push(block('getPenAttribute')); - blocks.push('-'); - blocks.push(block('changeSize')); - blocks.push(block('setSize')); - blocks.push('-'); - blocks.push(block('doStamp')); - blocks.push(block('floodFill')); - blocks.push(block('write')); - blocks.push('-'); - blocks.push(block('reportPenTrailsAsCostume')); - blocks.push('-'); - blocks.push(block('doPasteOn')); - blocks.push(block('doCutFrom')); - - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'control' || cat === 'unified') { - - blocks.push(block('receiveGo')); - blocks.push(block('receiveKey')); - blocks.push(block('receiveInteraction')); - blocks.push(block('receiveCondition')); - blocks.push(block('receiveMessage')); - blocks.push('-'); - blocks.push(block('doBroadcast')); - blocks.push(block('doBroadcastAndWait')); - blocks.push(block('doSend')); - blocks.push(watcherToggle('getLastMessage')); - blocks.push(block('getLastMessage')); - blocks.push('-'); - blocks.push(block('doWarp')); - blocks.push('-'); - blocks.push(block('doWait')); - blocks.push(block('doWaitUntil')); - blocks.push('-'); - blocks.push(block('doForever')); - blocks.push(block('doRepeat')); - blocks.push(block('doUntil')); - blocks.push(block('doFor')); - blocks.push('-'); - blocks.push(block('doIf')); - blocks.push(block('doIfElse')); - blocks.push(block('reportIfElse')); - blocks.push('-'); - blocks.push(block('doReport')); - blocks.push(block('doStopThis')); - blocks.push('-'); - blocks.push(block('doRun')); - blocks.push(block('fork')); - blocks.push(block('evaluate')); - blocks.push('-'); - blocks.push(block('doTellTo')); - blocks.push(block('reportAskFor')); - blocks.push('-'); - blocks.push(block('doCallCC')); - blocks.push(block('reportCallCC')); - blocks.push('-'); - blocks.push(block('receiveOnClone')); - blocks.push(block('createClone')); - blocks.push(block('newClone')); - blocks.push(block('removeClone')); - blocks.push('-'); - blocks.push(block('doPauseAll')); - - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'sensing' || cat === 'unified') { - - blocks.push(block('reportTouchingObject')); - blocks.push(block('reportTouchingColor')); - blocks.push(block('reportColorIsTouchingColor')); - blocks.push('-'); - blocks.push(block('doAsk')); - blocks.push(watcherToggle('getLastAnswer')); - blocks.push(block('getLastAnswer')); - blocks.push('-'); - blocks.push(watcherToggle('reportMouseX')); - blocks.push(block('reportMouseX')); - blocks.push(watcherToggle('reportMouseY')); - blocks.push(block('reportMouseY')); - blocks.push(block('reportMouseDown')); - blocks.push('-'); - blocks.push(block('reportKeyPressed')); - blocks.push('-'); - blocks.push(block('reportRelationTo')); - blocks.push(block('reportAspect')); - blocks.push('-'); - blocks.push(block('doResetTimer')); - blocks.push(watcherToggle('getTimer')); - blocks.push(block('getTimer')); - blocks.push('-'); - blocks.push(block('reportAttributeOf')); - - if (SpriteMorph.prototype.enableFirstClass) { - blocks.push(block('reportGet')); - } - - blocks.push(block('reportObject')); - blocks.push('-'); - blocks.push(block('reportURL')); - blocks.push(block('reportAudio')); - blocks.push(block('reportVideo')); - blocks.push(block('doSetVideoTransparency')); - blocks.push('-'); - blocks.push(block('reportGlobalFlag')); - blocks.push(block('doSetGlobalFlag')); - blocks.push('-'); - blocks.push(block('reportDate')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { + blocks.push(watcherToggle('xPosition')); + blocks.push(block('xPosition', this.inheritsAttribute('x position'))); + blocks.push(watcherToggle('yPosition')); + blocks.push(block('yPosition', this.inheritsAttribute('y position'))); + blocks.push(watcherToggle('direction')); + blocks.push(block('direction', this.inheritsAttribute('direction'))); + }, + looks: () => { + blocks.push(block('doSwitchToCostume')); + blocks.push(block('doWearNextCostume')); + blocks.push(watcherToggle('getCostumeIdx')); + blocks.push(block('getCostumeIdx', this.inheritsAttribute('costume #'))); blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(block('doSayFor')); + blocks.push(block('bubble')); + blocks.push(block('doThinkFor')); + blocks.push(block('doThink')); blocks.push('-'); - blocks.push(watcherToggle('reportThreadCount')); - blocks.push(block('reportThreadCount')); - blocks.push(block('reportStackSize')); - blocks.push(block('reportFrameCount')); - blocks.push(block('reportYieldCount')); - } + blocks.push(block('reportGetImageAttribute')); + blocks.push(block('reportNewCostumeStretched')); + blocks.push(block('reportNewCostume')); + blocks.push('-'); + blocks.push(block('changeEffect')); + blocks.push(block('setEffect')); + blocks.push(block('clearEffects')); + blocks.push(block('getEffect')); + blocks.push('-'); + blocks.push(block('changeScale')); + blocks.push(block('setScale')); + blocks.push(watcherToggle('getScale')); + blocks.push(block('getScale', this.inheritsAttribute('size'))); + blocks.push('-'); + blocks.push(block('show')); + blocks.push(block('hide')); + blocks.push(watcherToggle('reportShown')); + blocks.push(block('reportShown', this.inheritsAttribute('shown?'))); + blocks.push('-'); + blocks.push(block('goToLayer')); + blocks.push(block('goBack')); + blocks.push('-'); + blocks.push(block('doSwitchToScene')); - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'operators' || cat === 'unified') { + // for debugging: /////////////// - blocks.push(block('reifyScript')); - blocks.push(block('reifyReporter')); - blocks.push(block('reifyPredicate')); - blocks.push('#'); - blocks.push('-'); - blocks.push(block('reportSum')); - blocks.push(block('reportDifference')); - blocks.push(block('reportProduct')); - blocks.push(block('reportQuotient')); - blocks.push(block('reportPower')); - blocks.push('-'); - blocks.push(block('reportModulus')); - blocks.push(block('reportRound')); - blocks.push(block('reportMonadic')); - blocks.push(block('reportRandom')); - blocks.push('-'); - blocks.push(block('reportLessThan')); - blocks.push(block('reportEquals')); - blocks.push(block('reportGreaterThan')); - blocks.push('-'); - blocks.push(block('reportAnd')); - blocks.push(block('reportOr')); - blocks.push(block('reportNot')); - blocks.push(block('reportBoolean')); - blocks.push('-'); - blocks.push(block('reportJoinWords')); - blocks.push(block('reportTextSplit')); - blocks.push(block('reportLetter')); - blocks.push(block('reportStringSize')); - blocks.push('-'); - blocks.push(block('reportUnicode')); - blocks.push(block('reportUnicodeAsLetter')); - blocks.push('-'); - blocks.push(block('reportIsA')); - blocks.push(block('reportIsIdentical')); + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(block('log')); + blocks.push(block('alert')); + blocks.push('-'); + blocks.push(block('doScreenshot')); + } + }, - if (true) { // (Process.prototype.enableJS) { + sound: () => { + blocks.push(block('playSound')); + blocks.push(block('doPlaySoundUntilDone')); + blocks.push(block('doStopAllSounds')); + blocks.push('-'); + blocks.push(block('doPlaySoundAtRate')); + blocks.push(block('reportGetSoundAttribute')); + blocks.push(block('reportNewSoundFromSamples')); + blocks.push('-'); + blocks.push(block('doRest')); + blocks.push(block('doPlayNote')); + blocks.push(block('doSetInstrument')); + blocks.push('-'); + blocks.push(block('doChangeTempo')); + blocks.push(block('doSetTempo')); + blocks.push(watcherToggle('getTempo')); + blocks.push(block('getTempo')); + blocks.push('-'); + blocks.push(block('changeVolume')); + blocks.push(block('setVolume')); + blocks.push(watcherToggle('getVolume')); + blocks.push(block('getVolume', this.inheritsAttribute('volume'))); + blocks.push('-'); + blocks.push(block('changePan')); + blocks.push(block('setPan')); + blocks.push(watcherToggle('getPan')); + blocks.push(block('getPan', this.inheritsAttribute('balance'))); + blocks.push('-'); + blocks.push(block('playFreq')); + blocks.push(block('stopFreq')); + + // for debugging: /////////////// + + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(block('doPlayFrequency')); + } + }, + + pen: () => { + blocks.push(block('clear')); + blocks.push('-'); + blocks.push(block('down')); + blocks.push(block('up')); + blocks.push(watcherToggle('getPenDown')); + blocks.push(block('getPenDown', this.inheritsAttribute('pen down?'))); + blocks.push('-'); + blocks.push(block('setColor')); + blocks.push(block('changePenHSVA')); + blocks.push(block('setPenHSVA')); + blocks.push(block('getPenAttribute')); + blocks.push('-'); + blocks.push(block('changeSize')); + blocks.push(block('setSize')); + blocks.push('-'); + blocks.push(block('doStamp')); + blocks.push(block('floodFill')); + blocks.push(block('write')); + blocks.push('-'); + blocks.push(block('reportPenTrailsAsCostume')); + blocks.push('-'); + blocks.push(block('doPasteOn')); + blocks.push(block('doCutFrom')); + }, + + control: () => { + blocks.push(block('receiveGo')); + blocks.push(block('receiveKey')); + blocks.push(block('receiveInteraction')); + blocks.push(block('receiveCondition')); + blocks.push(block('receiveMessage')); + blocks.push('-'); + blocks.push(block('doBroadcast')); + blocks.push(block('doBroadcastAndWait')); + blocks.push(block('doSend')); + blocks.push(watcherToggle('getLastMessage')); + blocks.push(block('getLastMessage')); + blocks.push('-'); + blocks.push(block('doWarp')); + blocks.push('-'); + blocks.push(block('doWait')); + blocks.push(block('doWaitUntil')); + blocks.push('-'); + blocks.push(block('doForever')); + blocks.push(block('doRepeat')); + blocks.push(block('doUntil')); + blocks.push(block('doFor')); + blocks.push('-'); + blocks.push(block('doIf')); + blocks.push(block('doIfElse')); + blocks.push(block('reportIfElse')); + blocks.push('-'); + blocks.push(block('doReport')); + blocks.push(block('doStopThis')); + blocks.push('-'); + blocks.push(block('doRun')); + blocks.push(block('fork')); + blocks.push(block('evaluate')); + blocks.push('-'); + blocks.push(block('doTellTo')); + blocks.push(block('reportAskFor')); + blocks.push('-'); + blocks.push(block('doCallCC')); + blocks.push(block('reportCallCC')); + blocks.push('-'); + blocks.push(block('receiveOnClone')); + blocks.push(block('createClone')); + blocks.push(block('newClone')); + blocks.push(block('removeClone')); + blocks.push('-'); + blocks.push(block('doPauseAll')); + }, + + sensing: () => { + blocks.push(block('reportTouchingObject')); + blocks.push(block('reportTouchingColor')); + blocks.push(block('reportColorIsTouchingColor')); + blocks.push('-'); + blocks.push(block('doAsk')); + blocks.push(watcherToggle('getLastAnswer')); + blocks.push(block('getLastAnswer')); + blocks.push('-'); + blocks.push(watcherToggle('reportMouseX')); + blocks.push(block('reportMouseX')); + blocks.push(watcherToggle('reportMouseY')); + blocks.push(block('reportMouseY')); + blocks.push(block('reportMouseDown')); + blocks.push('-'); + blocks.push(block('reportKeyPressed')); + blocks.push('-'); + blocks.push(block('reportRelationTo')); + blocks.push(block('reportAspect')); + blocks.push('-'); + blocks.push(block('doResetTimer')); + blocks.push(watcherToggle('getTimer')); + blocks.push(block('getTimer')); + blocks.push('-'); + blocks.push(block('reportAttributeOf')); + + if (SpriteMorph.prototype.enableFirstClass) { + blocks.push(block('reportGet')); + } + + blocks.push(block('reportObject')); + blocks.push('-'); + blocks.push(block('reportURL')); + blocks.push(block('reportAudio')); + blocks.push(block('reportVideo')); + blocks.push(block('doSetVideoTransparency')); + blocks.push('-'); + blocks.push(block('reportGlobalFlag')); + blocks.push(block('doSetGlobalFlag')); + blocks.push('-'); + blocks.push(block('reportDate')); + + // for debugging: /////////////// + + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(watcherToggle('reportThreadCount')); + blocks.push(block('reportThreadCount')); + blocks.push(block('reportStackSize')); + blocks.push(block('reportFrameCount')); + blocks.push(block('reportYieldCount')); + } + }, + + operators: () => { + blocks.push(block('reifyScript')); + blocks.push(block('reifyReporter')); + blocks.push(block('reifyPredicate')); + blocks.push('#'); + blocks.push('-'); + blocks.push(block('reportSum')); + blocks.push(block('reportDifference')); + blocks.push(block('reportProduct')); + blocks.push(block('reportQuotient')); + blocks.push(block('reportPower')); + blocks.push('-'); + blocks.push(block('reportModulus')); + blocks.push(block('reportRound')); + blocks.push(block('reportMonadic')); + blocks.push(block('reportRandom')); + blocks.push('-'); + blocks.push(block('reportLessThan')); + blocks.push(block('reportEquals')); + blocks.push(block('reportGreaterThan')); + blocks.push('-'); + blocks.push(block('reportAnd')); + blocks.push(block('reportOr')); + blocks.push(block('reportNot')); + blocks.push(block('reportBoolean')); + blocks.push('-'); + blocks.push(block('reportJoinWords')); + blocks.push(block('reportTextSplit')); + blocks.push(block('reportLetter')); + blocks.push(block('reportStringSize')); + blocks.push('-'); + blocks.push(block('reportUnicode')); + blocks.push(block('reportUnicodeAsLetter')); + blocks.push('-'); + blocks.push(block('reportIsA')); + blocks.push(block('reportIsIdentical')); blocks.push('-'); blocks.push(block('reportJSFunction')); if (Process.prototype.enableCompiling) { - blocks.push(block('reportCompiled')); + blocks.push(block('reportCompiled')); } - } - // for debugging: /////////////// + // for debugging: /////////////// - if (this.world().isDevMode) { - blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); - blocks.push('-'); - blocks.push(block('reportTypeOf')); - blocks.push(block('reportTextFunction')); - } + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(block('reportTypeOf')); + blocks.push(block('reportTextFunction')); + } + }, - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'variables' || cat === 'unified') { - - button = new PushButtonMorph( - null, - function () { - new VariableDialogMorph( - null, - addVar, - myself - ).prompt( - 'Variable name', - null, - myself.world() - ); - }, - 'Make a variable' - ); - button.userMenu = helpMenu; - button.selector = 'addVariable'; - button.showHelp = BlockMorph.prototype.showHelp; - blocks.push(button); - - if (this.deletableVariableNames().length > 0) { + variables: () => { button = new PushButtonMorph( null, function () { - var menu = new MenuMorph( - myself.deleteVariable, + new VariableDialogMorph( null, + addVar, myself + ).prompt( + 'Variable name', + null, + myself.world() ); - myself.deletableVariableNames().forEach(name => - menu.addItem( - name, - name, - null, - null, - null, - null, - null, - null, - true // verbatim - don't translate - ) - ); - menu.popUpAtHand(myself.world()); }, - 'Delete a variable' + 'Make a variable' ); button.userMenu = helpMenu; - button.selector = 'deleteVariable'; + button.selector = 'addVariable'; button.showHelp = BlockMorph.prototype.showHelp; blocks.push(button); - } - blocks.push('-'); + if (this.deletableVariableNames().length > 0) { + button = new PushButtonMorph( + null, + function () { + var menu = new MenuMorph( + myself.deleteVariable, + null, + myself + ); + myself.deletableVariableNames().forEach(name => + menu.addItem( + name, + name, + null, + null, + null, + null, + null, + null, + true // verbatim - don't translate + ) + ); + menu.popUpAtHand(myself.world()); + }, + 'Delete a variable' + ); + button.userMenu = helpMenu; + button.selector = 'deleteVariable'; + button.showHelp = BlockMorph.prototype.showHelp; + blocks.push(button); + } - varNames = this.reachableGlobalVariableNames(true); - if (varNames.length > 0) { - varNames.forEach(name => { - blocks.push(variableWatcherToggle(name)); - blocks.push(variableBlock(name)); - }); blocks.push('-'); - } - varNames = this.allLocalVariableNames(true); - if (varNames.length > 0) { - varNames.forEach(name => { - blocks.push(variableWatcherToggle(name)); - blocks.push(variableBlock(name, true)); - }); - blocks.push('-'); - } + varNames = this.reachableGlobalVariableNames(true); + if (varNames.length > 0) { + varNames.forEach(name => { + blocks.push(variableWatcherToggle(name)); + blocks.push(variableBlock(name)); + }); + blocks.push('-'); + } - blocks.push(block('doSetVar')); - blocks.push(block('doChangeVar')); - blocks.push(block('doShowVar')); - blocks.push(block('doHideVar')); - blocks.push(block('doDeclareVariables')); + varNames = this.allLocalVariableNames(true); + if (varNames.length > 0) { + varNames.forEach(name => { + blocks.push(variableWatcherToggle(name)); + blocks.push(variableBlock(name, true)); + }); + blocks.push('-'); + } - // inheritance: + blocks.push(block('doSetVar')); + blocks.push(block('doChangeVar')); + blocks.push(block('doShowVar')); + blocks.push(block('doHideVar')); + blocks.push(block('doDeclareVariables')); - if (StageMorph.prototype.enableInheritance) { - blocks.push('-'); - blocks.push(block('doDeleteAttr')); - } + // inheritance: - /////////////////////////////// + if (StageMorph.prototype.enableInheritance) { + blocks.push('-'); + blocks.push(block('doDeleteAttr')); + } - blocks.push('='); + /////////////////////////////// - blocks.push(block('reportNewList')); - blocks.push(block('reportNumbers')); - blocks.push('-'); - blocks.push(block('reportCONS')); - blocks.push(block('reportListItem')); - blocks.push(block('reportCDR')); - blocks.push('-'); - blocks.push(block('reportListAttribute')); - blocks.push(block('reportListIndex')); - blocks.push(block('reportListContainsItem')); - blocks.push(block('reportListIsEmpty')); - blocks.push('-'); - blocks.push(block('reportMap')); - blocks.push(block('reportKeep')); - blocks.push(block('reportFindFirst')); - blocks.push(block('reportCombine')); - blocks.push('-'); - blocks.push(block('doForEach')); - blocks.push('-'); - blocks.push(block('reportConcatenatedLists')); - blocks.push(block('reportReshape')); - blocks.push('-'); - blocks.push(block('doAddToList')); - blocks.push(block('doDeleteFromList')); - blocks.push(block('doInsertInList')); - blocks.push(block('doReplaceInList')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { - blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); - blocks.push('-'); - blocks.push(block('doShowTable')); - } - - if (StageMorph.prototype.enableCodeMapping) { blocks.push('='); - blocks.push(block('doMapCodeOrHeader')); - blocks.push(block('doMapValueCode')); - blocks.push(block('doMapListCode')); + + blocks.push(block('reportNewList')); + blocks.push(block('reportNumbers')); blocks.push('-'); - blocks.push(block('reportMappedCode')); + blocks.push(block('reportCONS')); + blocks.push(block('reportListItem')); + blocks.push(block('reportCDR')); + blocks.push('-'); + blocks.push(block('reportListAttribute')); + blocks.push(block('reportListIndex')); + blocks.push(block('reportListContainsItem')); + blocks.push(block('reportListIsEmpty')); + blocks.push('-'); + blocks.push(block('reportMap')); + blocks.push(block('reportKeep')); + blocks.push(block('reportFindFirst')); + blocks.push(block('reportCombine')); + blocks.push('-'); + blocks.push(block('doForEach')); + blocks.push('-'); + blocks.push(block('reportConcatenatedLists')); + blocks.push(block('reportReshape')); + blocks.push('-'); + blocks.push(block('doAddToList')); + blocks.push(block('doDeleteFromList')); + blocks.push(block('doInsertInList')); + blocks.push(block('doReplaceInList')); + + // for debugging: /////////////// + + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(block('doShowTable')); + } + + if (StageMorph.prototype.enableCodeMapping) { + blocks.push('='); + blocks.push(block('doMapCodeOrHeader')); + blocks.push(block('doMapValueCode')); + blocks.push(block('doMapListCode')); + blocks.push('-'); + blocks.push(block('reportMappedCode')); + } } } - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); + // In a Unified Palette custom blocks appear following each category, + // but there is only 1 make a block button (at the end). + if (cat === 'unified') { + for (currentCategory in paletteDefitions) { + paletteDefitions[currentCategory](); + blocks.push('='); + + blocks.push(...this.customBlockTemplatesForCategory(currentCategory)); + } + blocks.push(this.makeBlockButton()); + } else { + paletteDefitions[cat](); + + blocks.push('='); + blocks.push(this.makeBlockButton(cat)); + blocks.push('='); + blocks.push(...this.customBlockTemplatesForCategory(cat)); + } + + return blocks; +}; + +// returns an array all custom block templates for a selected category. +SpriteMorph.prototype.customBlockTemplatesForCategory = function (category) { + var ide = this.parentThatIsA(IDE_Morph), blocks = [], + isInherited = false, block, inheritedBlocks; + + function addCustomBlock(definition) { + if (category === 'unified' || definition.category === category || + (category === 'variables' + && contains(['lists', 'other'], definition.category))) { + block = definition.templateInstance(); + if (isInherited) {block.ghost(); } + blocks.push(block); + } + } + + // global custom blocks: + if (ide && ide.stage) { + ide.stage.globalBlocks.forEach(addCustomBlock); + if (this.customBlocks.length) {blocks.push('='); } + } + + // local custom blocks: + this.customBlocks.forEach(addCustomBlock); + + // inherited custom blocks: + if (this.exemplar) { + inheritedBlocks = this.inheritedBlocks(true); + if (this.customBlocks.length && inheritedBlocks.length) {blocks.push('='); } + isInherited = true; + inheritedBlocks.forEach(addCustomBlock); + } return blocks; }; @@ -2869,6 +2884,7 @@ SpriteMorph.prototype.makeBlock = function () { category = ide.currentCategory, clr = SpriteMorph.prototype.blockColor[category], dlg; + debugger; dlg = new BlockDialogMorph( null, definition => { @@ -2878,7 +2894,7 @@ SpriteMorph.prototype.makeBlock = function () { } else { this.customBlocks.push(definition); } - ide.flushPaletteCache(); + ide.flushBlocksCache(); ide.refreshPalette(); ide.recordUnsavedChanges(); new BlockEditorMorph(definition, this).popUp(); @@ -2886,7 +2902,7 @@ SpriteMorph.prototype.makeBlock = function () { }, this ); - if (category !== 'variables') { + if (category !== 'variables' || category !== 'unified') { dlg.category = category; dlg.categories.children.forEach(each => each.refresh()); dlg.types.children.forEach(each => { @@ -3066,8 +3082,6 @@ SpriteMorph.prototype.freshPalette = function (category) { return menu; }; - // primitives: - blocks = this.blocksCache[category]; if (!blocks) { blocks = this.blockTemplates(category); @@ -3109,74 +3123,6 @@ SpriteMorph.prototype.freshPalette = function (category) { } }); - // global custom blocks: - - if (stage) { - y += unit * 1.6; - - stage.globalBlocks.forEach(definition => { - var block; - if (definition.category === category || - (category === 'variables' - && contains( - ['lists', 'other'], - definition.category - ))) { - block = definition.templateInstance(); - y += unit * 0.3; - block.setPosition(new Point(x, y)); - palette.addContents(block); - x = 0; - y += block.height(); - } - }); - } - - // local custom blocks: - - y += unit * 1.6; - this.customBlocks.forEach(definition => { - var block; - if (definition.category === category || - (category === 'variables' - && contains( - ['lists', 'other'], - definition.category - ))) { - block = definition.templateInstance(); - y += unit * 0.3; - block.setPosition(new Point(x, y)); - palette.addContents(block); - x = 0; - y += block.height(); - } - }); - - // inherited custom blocks: - - // y += unit * 1.6; - if (this.exemplar) { - this.inheritedBlocks(true).forEach(definition => { - var block; - if (definition.category === category || - (category === 'variables' - && contains( - ['lists', 'other'], - definition.category - ))) { - block = definition.templateInstance(); - y += unit * 0.3; - block.setPosition(new Point(x, y)); - palette.addContents(block); - block.ghost(); - x = 0; - y += block.height(); - } - }); - } - - //layout - palette.scrollX(palette.padding); palette.scrollY(palette.padding); return palette; From 363666e3de0c0e6189670642e10c96831b235f9a Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 29 Jun 2021 17:48:46 -0700 Subject: [PATCH 80/92] WIP loading custom blocks works in unified mode. --- src/gui.js | 1 - src/objects.js | 55 ++++++++++++++++++++++++-------------------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/gui.js b/src/gui.js index fb3b067e..c0b7e449 100644 --- a/src/gui.js +++ b/src/gui.js @@ -5765,7 +5765,6 @@ IDE_Morph.prototype.switchToDevMode = function () { }; IDE_Morph.prototype.flushBlocksCache = function (category) { - alert(`blocks cacbe flushed for ${category}`); // if no category is specified, the whole cache gets flushed // the 'unified' category is always flushed. if (category) { diff --git a/src/objects.js b/src/objects.js index d0efe4bd..f2386f56 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2567,7 +2567,6 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('doPauseAll')); }, -<<<<<<< HEAD sensing: () => { blocks.push(block('reportTouchingObject')); blocks.push(block('reportTouchingColor')); @@ -2584,9 +2583,6 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportMouseDown')); blocks.push('-'); blocks.push(block('reportKeyPressed')); -======= - if (Process.prototype.enableJS) { ->>>>>>> upstream/scenes blocks.push('-'); blocks.push(block('reportRelationTo')); blocks.push(block('reportAspect')); @@ -2663,12 +2659,14 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('reportIsA')); blocks.push(block('reportIsIdentical')); - blocks.push('-'); - blocks.push(block('reportJSFunction')); - if (Process.prototype.enableCompiling) { - blocks.push(block('reportCompiled')); - } + if (Process.prototype.enableJS) { + blocks.push('-'); + blocks.push(block('reportJSFunction')); + if (Process.prototype.enableCompiling) { + blocks.push(block('reportCompiled')); + } + } // for debugging: /////////////// if (this.world().isDevMode) { @@ -2777,7 +2775,6 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportListItem')); blocks.push(block('reportCDR')); blocks.push('-'); -<<<<<<< HEAD blocks.push(block('reportListAttribute')); blocks.push(block('reportListIndex')); blocks.push(block('reportListContainsItem')); @@ -2815,12 +2812,10 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('reportMappedCode')); } -======= blocks.push(block('doShowTable')); blocks.push('-'); blocks.push(block('doApplyExtension')); blocks.push(block('reportApplyExtension')); ->>>>>>> upstream/scenes } } @@ -2831,16 +2826,10 @@ SpriteMorph.prototype.blockTemplates = function (category) { paletteDefitions[currentCategory](); blocks.push('='); - blocks.push(...this.customBlockTemplatesForCategory(currentCategory)); } blocks.push(this.makeBlockButton()); } else { paletteDefitions[cat](); - - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - blocks.push('='); - blocks.push(...this.customBlockTemplatesForCategory(cat)); } return blocks; @@ -2882,10 +2871,10 @@ SpriteMorph.prototype.customBlockTemplatesForCategory = function (category) { }; SpriteMorph.prototype.makeBlockButton = function (category) { - // answer a button that prompts the user to make a new block + // answer a button that prompts the user to make a new block var button = new PushButtonMorph( this, - 'makeBlock', + 'makeBlock', 'Make a block' ); @@ -2907,7 +2896,6 @@ SpriteMorph.prototype.makeBlock = function () { category = ide.currentCategory, clr = SpriteMorph.prototype.blockColor[category], dlg; - debugger; dlg = new BlockDialogMorph( null, definition => { @@ -2940,6 +2928,17 @@ SpriteMorph.prototype.makeBlock = function () { ); }; +SpriteMorph.prototype.getPrimitiveTemplates = function (category) { + blocks = this.blocksCache[category]; + if (!blocks) { + blocks = this.blockTemplates(category); + if (this.isCachingPrimitives) { + this.blocksCache[category] = blocks; + } + } + return blocks; +} + SpriteMorph.prototype.palette = function (category) { if (!this.paletteCache[category]) { this.paletteCache[category] = this.freshPalette(category); @@ -3105,13 +3104,11 @@ SpriteMorph.prototype.freshPalette = function (category) { return menu; }; - blocks = this.blocksCache[category]; - if (!blocks) { - blocks = this.blockTemplates(category); - if (this.isCachingPrimitives) { - this.blocksCache[category] = blocks; - } - } + blocks = this.getPrimitiveTemplates(category); + blocks.push('='); + blocks.push(this.makeBlockButton(cat)); + blocks.push('='); + blocks.push(...this.customBlockTemplatesForCategory(cat)); blocks.forEach(block => { if (block === null) { @@ -8813,7 +8810,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportIsA')); blocks.push(block('reportIsIdentical')); - if (Process.prototype.enableJS) { + if (Process.prototype.enableJS) { // (Process.prototype.enableJS) { blocks.push('-'); blocks.push(block('reportJSFunction')); if (Process.prototype.enableCompiling) { From 529123f6a9a7331fc44b1def99d892e3a66958d3 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 29 Jun 2021 17:49:55 -0700 Subject: [PATCH 81/92] rename blocksCache primitivesCache for clarity --- src/gui.js | 22 +++++++++++----------- src/objects.js | 14 +++++++------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/gui.js b/src/gui.js index c0b7e449..4c6fd852 100644 --- a/src/gui.js +++ b/src/gui.js @@ -3663,7 +3663,7 @@ IDE_Morph.prototype.settingsMenu = function () { } */ Process.prototype.enableJS = !Process.prototype.enableJS; - this.currentSprite.blocksCache.operators = null; + this.currentSprite.primitivesCache.operators = null; this.currentSprite.paletteCache.operators = null; this.refreshPalette(); }, @@ -3904,7 +3904,7 @@ IDE_Morph.prototype.settingsMenu = function () { () => { SpriteMorph.prototype.enableFirstClass = !SpriteMorph.prototype.enableFirstClass; - this.currentSprite.blocksCache.sensing = null; + this.currentSprite.primitivesCache.sensing = null; this.currentSprite.paletteCache.sensing = null; this.refreshPalette(); }, @@ -3978,7 +3978,7 @@ IDE_Morph.prototype.settingsMenu = function () { () => { Process.prototype.enableCompiling = !Process.prototype.enableCompiling; - this.currentSprite.blocksCache.operators = null; + this.currentSprite.primitivesCache.operators = null; this.currentSprite.paletteCache.operators = null; this.refreshPalette(); }, @@ -4016,7 +4016,7 @@ IDE_Morph.prototype.settingsMenu = function () { () => { StageMorph.prototype.enableCodeMapping = !StageMorph.prototype.enableCodeMapping; - this.currentSprite.blocksCache.variables = null; + this.currentSprite.primitivesCache.variables = null; this.currentSprite.paletteCache.variables = null; this.refreshPalette(); }, @@ -4030,7 +4030,7 @@ IDE_Morph.prototype.settingsMenu = function () { () => { StageMorph.prototype.enableInheritance = !StageMorph.prototype.enableInheritance; - this.currentSprite.blocksCache.variables = null; + this.currentSprite.primitivesCache.variables = null; this.currentSprite.paletteCache.variables = null; this.refreshPalette(); }, @@ -5768,19 +5768,19 @@ IDE_Morph.prototype.flushBlocksCache = function (category) { // if no category is specified, the whole cache gets flushed // the 'unified' category is always flushed. if (category) { - this.stage.blocksCache[category] = null; - this.stage.blocksCache.unified = null; + this.stage.primitivesCache[category] = null; + this.stage.primitivesCache.unified = null; this.stage.children.forEach(m => { if (m instanceof SpriteMorph) { - m.blocksCache[category] = null; - m.blocksCache.unified = null; + m.primitivesCache[category] = null; + m.primitivesCache.unified = null; } }); } else { - this.stage.blocksCache = {}; + this.stage.primitivesCache = {}; this.stage.children.forEach(m => { if (m instanceof SpriteMorph) { - m.blocksCache = {}; + m.primitivesCache = {}; } }); } diff --git a/src/objects.js b/src/objects.js index f2386f56..cdc5058f 100644 --- a/src/objects.js +++ b/src/objects.js @@ -1837,7 +1837,7 @@ SpriteMorph.prototype.init = function (globals) { this.rotatesWithAnchor = true; this.layers = null; // cache for dragging nested sprites, don't serialize - this.blocksCache = {}; // not to be serialized (!) + this.primitivesCache = {}; // not to be serialized (!) this.paletteCache = {}; // not to be serialized (!) this.rotationOffset = ZERO; // not to be serialized (!) this.idx = 0; // not to be serialized (!) - used for de-serialization @@ -1902,7 +1902,7 @@ SpriteMorph.prototype.fullCopy = function (forClone) { c.gainNode = null; c.pannerNode = null; c.freqPlayer = null; - c.blocksCache = {}; + c.primitivesCache = {}; c.paletteCache = {}; c.imageData = {}; c.cachedHSV = c.color.hsv(); @@ -2929,11 +2929,11 @@ SpriteMorph.prototype.makeBlock = function () { }; SpriteMorph.prototype.getPrimitiveTemplates = function (category) { - blocks = this.blocksCache[category]; + blocks = this.primitivesCache[category]; if (!blocks) { blocks = this.blockTemplates(category); if (this.isCachingPrimitives) { - this.blocksCache[category] = blocks; + this.primitivesCache[category] = blocks; } } return blocks; @@ -3589,7 +3589,7 @@ SpriteMorph.prototype.addVariable = function (name, isGlobal) { } } else { this.variables.addVar(name); - this.blocksCache.variables = null; + this.primitivesCache.variables = null; } }; @@ -7691,7 +7691,7 @@ StageMorph.prototype.init = function (globals) { this.cachedHSV = [0, 0, 0]; // for background hsv support, not serialized this.keysPressed = {}; // for handling keyboard events, do not persist - this.blocksCache = {}; // not to be serialized (!) + this.primitivesCache = {}; // not to be serialized (!) this.paletteCache = {}; // not to be serialized (!) this.lastAnswer = ''; // last user input, do not persist this.activeSounds = []; // do not persist @@ -8543,7 +8543,7 @@ StageMorph.prototype.blockTemplates = function (category) { ide = myself.parentThatIsA(IDE_Morph); myself.addVariable(pair[0], pair[1]); myself.toggleVariableWatcher(pair[0], pair[1]); - myself.blocksCache[cat] = null; + myself.primitivesCache[cat] = null; myself.paletteCache[cat] = null; ide.refreshPalette(); ide.recordUnsavedChanges(); From 27c116bee43603ddfac6e4eee0d69e05af849058 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 29 Jun 2021 19:59:52 -0700 Subject: [PATCH 82/92] Refactor blockTemplates and palette drawing. Extract functions from blockTemplates --- src/objects.js | 1105 ++++++++++++++++++++++-------------------------- 1 file changed, 502 insertions(+), 603 deletions(-) diff --git a/src/objects.js b/src/objects.js index cdc5058f..9a257b7d 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2286,8 +2286,8 @@ SpriteMorph.prototype.variableBlock = function (varName, isLocalTemplate) { // SpriteMorph block templates SpriteMorph.prototype.blockTemplates = function (category) { - var blocks = [], myself = this, varNames, button, - cat = category || 'motion', paletteDefitions; + var blocks = [], myself = this, varNames, + category = category || 'motion'; function block(selector, isGhosted) { if (StageMorph.prototype.hiddenPrimitives[selector]) { @@ -2347,18 +2347,396 @@ SpriteMorph.prototype.blockTemplates = function (category) { ); } - function helpMenu() { - var menu = new MenuMorph(this); - menu.addItem('help...', 'showHelp'); - return menu; + if (category === 'motion') { + + blocks.push(block('forward')); + blocks.push(block('turn')); + blocks.push(block('turnLeft')); + blocks.push('-'); + blocks.push(block('setHeading')); + blocks.push(block('doFaceTowards')); + blocks.push('-'); + blocks.push(block('gotoXY')); + blocks.push(block('doGotoObject')); + blocks.push(block('doGlide')); + blocks.push('-'); + blocks.push(block('changeXPosition')); + blocks.push(block('setXPosition')); + blocks.push(block('changeYPosition')); + blocks.push(block('setYPosition')); + blocks.push('-'); + blocks.push(block('bounceOffEdge')); + blocks.push('-'); + blocks.push(watcherToggle('xPosition')); + blocks.push(block('xPosition', this.inheritsAttribute('x position'))); + blocks.push(watcherToggle('yPosition')); + blocks.push(block('yPosition', this.inheritsAttribute('y position'))); + blocks.push(watcherToggle('direction')); + blocks.push(block('direction', this.inheritsAttribute('direction'))); + + } else if (category === 'looks') { + + blocks.push(block('doSwitchToCostume')); + blocks.push(block('doWearNextCostume')); + blocks.push(watcherToggle('getCostumeIdx')); + blocks.push(block('getCostumeIdx', this.inheritsAttribute('costume #'))); + blocks.push('-'); + blocks.push(block('doSayFor')); + blocks.push(block('bubble')); + blocks.push(block('doThinkFor')); + blocks.push(block('doThink')); + blocks.push('-'); + blocks.push(block('reportGetImageAttribute')); + blocks.push(block('reportNewCostumeStretched')); + blocks.push(block('reportNewCostume')); + blocks.push('-'); + blocks.push(block('changeEffect')); + blocks.push(block('setEffect')); + blocks.push(block('clearEffects')); + blocks.push(block('getEffect')); + blocks.push('-'); + blocks.push(block('changeScale')); + blocks.push(block('setScale')); + blocks.push(watcherToggle('getScale')); + blocks.push(block('getScale', this.inheritsAttribute('size'))); + blocks.push('-'); + blocks.push(block('show')); + blocks.push(block('hide')); + blocks.push(watcherToggle('reportShown')); + blocks.push(block('reportShown', this.inheritsAttribute('shown?'))); + blocks.push('-'); + blocks.push(block('goToLayer')); + blocks.push(block('goBack')); + blocks.push('-'); + blocks.push(block('doSwitchToScene')); + + // for debugging: /////////////// + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(this.devModeText()); + blocks.push('-'); + blocks.push(block('log')); + blocks.push(block('alert')); + blocks.push('-'); + blocks.push(block('doScreenshot')); + } + + } else if (category === 'sound') { + + blocks.push(block('playSound')); + blocks.push(block('doPlaySoundUntilDone')); + blocks.push(block('doStopAllSounds')); + blocks.push('-'); + blocks.push(block('doPlaySoundAtRate')); + blocks.push(block('reportGetSoundAttribute')); + blocks.push(block('reportNewSoundFromSamples')); + blocks.push('-'); + blocks.push(block('doRest')); + blocks.push(block('doPlayNote')); + blocks.push(block('doSetInstrument')); + blocks.push('-'); + blocks.push(block('doChangeTempo')); + blocks.push(block('doSetTempo')); + blocks.push(watcherToggle('getTempo')); + blocks.push(block('getTempo')); + blocks.push('-'); + blocks.push(block('changeVolume')); + blocks.push(block('setVolume')); + blocks.push(watcherToggle('getVolume')); + blocks.push(block('getVolume', this.inheritsAttribute('volume'))); + blocks.push('-'); + blocks.push(block('changePan')); + blocks.push(block('setPan')); + blocks.push(watcherToggle('getPan')); + blocks.push(block('getPan', this.inheritsAttribute('balance'))); + blocks.push('-'); + blocks.push(block('playFreq')); + blocks.push(block('stopFreq')); + + // for debugging: /////////////// + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(block('doPlayFrequency')); + } + + } else if (category === 'pen') { + + blocks.push(block('clear')); + blocks.push('-'); + blocks.push(block('down')); + blocks.push(block('up')); + blocks.push(watcherToggle('getPenDown')); + blocks.push(block('getPenDown', this.inheritsAttribute('pen down?'))); + blocks.push('-'); + blocks.push(block('setColor')); + blocks.push(block('changePenHSVA')); + blocks.push(block('setPenHSVA')); + blocks.push(block('getPenAttribute')); + blocks.push('-'); + blocks.push(block('changeSize')); + blocks.push(block('setSize')); + blocks.push('-'); + blocks.push(block('doStamp')); + blocks.push(block('floodFill')); + blocks.push(block('write')); + blocks.push('-'); + blocks.push(block('reportPenTrailsAsCostume')); + blocks.push('-'); + blocks.push(block('doPasteOn')); + blocks.push(block('doCutFrom')); + + } else if (category === 'control') { + + blocks.push(block('receiveGo')); + blocks.push(block('receiveKey')); + blocks.push(block('receiveInteraction')); + blocks.push(block('receiveCondition')); + blocks.push(block('receiveMessage')); + blocks.push('-'); + blocks.push(block('doBroadcast')); + blocks.push(block('doBroadcastAndWait')); + blocks.push(block('doSend')); + blocks.push(watcherToggle('getLastMessage')); + blocks.push(block('getLastMessage')); + blocks.push('-'); + blocks.push(block('doWarp')); + blocks.push('-'); + blocks.push(block('doWait')); + blocks.push(block('doWaitUntil')); + blocks.push('-'); + blocks.push(block('doForever')); + blocks.push(block('doRepeat')); + blocks.push(block('doUntil')); + blocks.push(block('doFor')); + blocks.push('-'); + blocks.push(block('doIf')); + blocks.push(block('doIfElse')); + blocks.push(block('reportIfElse')); + blocks.push('-'); + blocks.push(block('doReport')); + blocks.push(block('doStopThis')); + blocks.push('-'); + blocks.push(block('doRun')); + blocks.push(block('fork')); + blocks.push(block('evaluate')); + blocks.push('-'); + blocks.push(block('doTellTo')); + blocks.push(block('reportAskFor')); + blocks.push('-'); + blocks.push(block('doCallCC')); + blocks.push(block('reportCallCC')); + blocks.push('-'); + blocks.push(block('receiveOnClone')); + blocks.push(block('createClone')); + blocks.push(block('newClone')); + blocks.push(block('removeClone')); + blocks.push('-'); + blocks.push(block('doPauseAll')); + + } else if (category === 'sensing') { + + blocks.push(block('reportTouchingObject')); + blocks.push(block('reportTouchingColor')); + blocks.push(block('reportColorIsTouchingColor')); + blocks.push('-'); + blocks.push(block('doAsk')); + blocks.push(watcherToggle('getLastAnswer')); + blocks.push(block('getLastAnswer')); + blocks.push('-'); + blocks.push(watcherToggle('reportMouseX')); + blocks.push(block('reportMouseX')); + blocks.push(watcherToggle('reportMouseY')); + blocks.push(block('reportMouseY')); + blocks.push(block('reportMouseDown')); + blocks.push('-'); + blocks.push(block('reportKeyPressed')); + blocks.push('-'); + blocks.push(block('reportRelationTo')); + blocks.push(block('reportAspect')); + blocks.push('-'); + blocks.push(block('doResetTimer')); + blocks.push(watcherToggle('getTimer')); + blocks.push(block('getTimer')); + blocks.push('-'); + blocks.push(block('reportAttributeOf')); + + if (SpriteMorph.prototype.enableFirstClass) { + blocks.push(block('reportGet')); + } + + blocks.push(block('reportObject')); + blocks.push('-'); + blocks.push(block('reportURL')); + blocks.push(block('reportAudio')); + blocks.push(block('reportVideo')); + blocks.push(block('doSetVideoTransparency')); + blocks.push('-'); + blocks.push(block('reportGlobalFlag')); + blocks.push(block('doSetGlobalFlag')); + blocks.push('-'); + blocks.push(block('reportDate')); + + // for debugging: /////////////// + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(this.devModeText()); + blocks.push('-'); + blocks.push(watcherToggle('reportThreadCount')); + blocks.push(block('reportThreadCount')); + blocks.push(block('reportStackSize')); + blocks.push(block('reportFrameCount')); + blocks.push(block('reportYieldCount')); + } + } else if (category === 'operators') { + + blocks.push(block('reifyScript')); + blocks.push(block('reifyReporter')); + blocks.push(block('reifyPredicate')); + blocks.push('#'); + blocks.push('-'); + blocks.push(block('reportSum')); + blocks.push(block('reportDifference')); + blocks.push(block('reportProduct')); + blocks.push(block('reportQuotient')); + blocks.push(block('reportPower')); + blocks.push('-'); + blocks.push(block('reportModulus')); + blocks.push(block('reportRound')); + blocks.push(block('reportMonadic')); + blocks.push(block('reportRandom')); + blocks.push('-'); + blocks.push(block('reportLessThan')); + blocks.push(block('reportEquals')); + blocks.push(block('reportGreaterThan')); + blocks.push('-'); + blocks.push(block('reportAnd')); + blocks.push(block('reportOr')); + blocks.push(block('reportNot')); + blocks.push(block('reportBoolean')); + blocks.push('-'); + blocks.push(block('reportJoinWords')); + blocks.push(block('reportTextSplit')); + blocks.push(block('reportLetter')); + blocks.push(block('reportStringSize')); + blocks.push('-'); + blocks.push(block('reportUnicode')); + blocks.push(block('reportUnicodeAsLetter')); + blocks.push('-'); + blocks.push(block('reportIsA')); + blocks.push(block('reportIsIdentical')); + + if (Process.prototype.enableJS) { + blocks.push('-'); + blocks.push(block('reportJSFunction')); + if (Process.prototype.enableCompiling) { + blocks.push(block('reportCompiled')); + } + } + // for debugging: /////////////// + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(block('reportTypeOf')); + blocks.push(block('reportTextFunction')); + } + + } else if (category === 'variables') { + + blocks.push(this.makeAVariableButton()); + if (this.deletableVariableNames().length > 0) { + blocks.push(this.deleteVariableButton()); + } + blocks.push('-'); + + varNames = this.reachableGlobalVariableNames(true); + if (varNames.length > 0) { + varNames.forEach(name => { + blocks.push(variableWatcherToggle(name)); + blocks.push(variableBlock(name)); + }); + blocks.push('-'); + } + + varNames = this.allLocalVariableNames(true); + if (varNames.length > 0) { + varNames.forEach(name => { + blocks.push(variableWatcherToggle(name)); + blocks.push(variableBlock(name, true)); + }); + blocks.push('-'); + } + + blocks.push(block('doSetVar')); + blocks.push(block('doChangeVar')); + blocks.push(block('doShowVar')); + blocks.push(block('doHideVar')); + blocks.push(block('doDeclareVariables')); + + // inheritance: + + if (StageMorph.prototype.enableInheritance) { + blocks.push('-'); + blocks.push(block('doDeleteAttr')); + } + + blocks.push('='); + blocks.push(block('reportNewList')); + blocks.push(block('reportNumbers')); + blocks.push('-'); + blocks.push(block('reportCONS')); + blocks.push(block('reportListItem')); + blocks.push(block('reportCDR')); + blocks.push('-'); + blocks.push(block('reportListAttribute')); + blocks.push(block('reportListIndex')); + blocks.push(block('reportListContainsItem')); + blocks.push(block('reportListIsEmpty')); + blocks.push('-'); + blocks.push(block('reportMap')); + blocks.push(block('reportKeep')); + blocks.push(block('reportFindFirst')); + blocks.push(block('reportCombine')); + blocks.push('-'); + blocks.push(block('doForEach')); + blocks.push('-'); + blocks.push(block('reportConcatenatedLists')); + blocks.push(block('reportReshape')); + blocks.push('-'); + blocks.push(block('doAddToList')); + blocks.push(block('doDeleteFromList')); + blocks.push(block('doInsertInList')); + blocks.push(block('doReplaceInList')); + + // for debugging: /////////////// + if (this.world().isDevMode) { + blocks.push('-'); + blocks.push(devModeText()); + blocks.push('-'); + blocks.push(block('doShowTable')); + blocks.push('-'); + blocks.push(block('doApplyExtension')); + blocks.push(block('reportApplyExtension')); + } + + if (StageMorph.prototype.enableCodeMapping) { + blocks.push('='); + blocks.push(block('doMapCodeOrHeader')); + blocks.push(block('doMapValueCode')); + blocks.push(block('doMapListCode')); + blocks.push('-'); + blocks.push(block('reportMappedCode')); + } } - function devModeText() { - let txt = new TextMorph(localize('development mode \ndebugging primitives:')); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - return txt; - } + return blocks; +}; + +// Utitlies displayed in the palette +SpriteMorph.prototype.makeAVariableButton = function() { + let button, myself = this; function addVar(pair) { var ide; @@ -2376,466 +2754,75 @@ SpriteMorph.prototype.blockTemplates = function (category) { } } - paletteDefitions = { - motion: () => { - blocks.push(block('forward')); - blocks.push(block('turn')); - blocks.push(block('turnLeft')); - blocks.push('-'); - blocks.push(block('setHeading')); - blocks.push(block('doFaceTowards')); - blocks.push('-'); - blocks.push(block('gotoXY')); - blocks.push(block('doGotoObject')); - blocks.push(block('doGlide')); - blocks.push('-'); - blocks.push(block('changeXPosition')); - blocks.push(block('setXPosition')); - blocks.push(block('changeYPosition')); - blocks.push(block('setYPosition')); - blocks.push('-'); - blocks.push(block('bounceOffEdge')); - blocks.push('-'); - blocks.push(watcherToggle('xPosition')); - blocks.push(block('xPosition', this.inheritsAttribute('x position'))); - blocks.push(watcherToggle('yPosition')); - blocks.push(block('yPosition', this.inheritsAttribute('y position'))); - blocks.push(watcherToggle('direction')); - blocks.push(block('direction', this.inheritsAttribute('direction'))); - }, - - looks: () => { - blocks.push(block('doSwitchToCostume')); - blocks.push(block('doWearNextCostume')); - blocks.push(watcherToggle('getCostumeIdx')); - blocks.push(block('getCostumeIdx', this.inheritsAttribute('costume #'))); - blocks.push('-'); - blocks.push(block('doSayFor')); - blocks.push(block('bubble')); - blocks.push(block('doThinkFor')); - blocks.push(block('doThink')); - blocks.push('-'); - blocks.push(block('reportGetImageAttribute')); - blocks.push(block('reportNewCostumeStretched')); - blocks.push(block('reportNewCostume')); - blocks.push('-'); - blocks.push(block('changeEffect')); - blocks.push(block('setEffect')); - blocks.push(block('clearEffects')); - blocks.push(block('getEffect')); - blocks.push('-'); - blocks.push(block('changeScale')); - blocks.push(block('setScale')); - blocks.push(watcherToggle('getScale')); - blocks.push(block('getScale', this.inheritsAttribute('size'))); - blocks.push('-'); - blocks.push(block('show')); - blocks.push(block('hide')); - blocks.push(watcherToggle('reportShown')); - blocks.push(block('reportShown', this.inheritsAttribute('shown?'))); - blocks.push('-'); - blocks.push(block('goToLayer')); - blocks.push(block('goBack')); - blocks.push('-'); - blocks.push(block('doSwitchToScene')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { - blocks.push('-'); - blocks.push(devModeText()); - blocks.push('-'); - blocks.push(block('log')); - blocks.push(block('alert')); - blocks.push('-'); - blocks.push(block('doScreenshot')); - } - }, - - sound: () => { - blocks.push(block('playSound')); - blocks.push(block('doPlaySoundUntilDone')); - blocks.push(block('doStopAllSounds')); - blocks.push('-'); - blocks.push(block('doPlaySoundAtRate')); - blocks.push(block('reportGetSoundAttribute')); - blocks.push(block('reportNewSoundFromSamples')); - blocks.push('-'); - blocks.push(block('doRest')); - blocks.push(block('doPlayNote')); - blocks.push(block('doSetInstrument')); - blocks.push('-'); - blocks.push(block('doChangeTempo')); - blocks.push(block('doSetTempo')); - blocks.push(watcherToggle('getTempo')); - blocks.push(block('getTempo')); - blocks.push('-'); - blocks.push(block('changeVolume')); - blocks.push(block('setVolume')); - blocks.push(watcherToggle('getVolume')); - blocks.push(block('getVolume', this.inheritsAttribute('volume'))); - blocks.push('-'); - blocks.push(block('changePan')); - blocks.push(block('setPan')); - blocks.push(watcherToggle('getPan')); - blocks.push(block('getPan', this.inheritsAttribute('balance'))); - blocks.push('-'); - blocks.push(block('playFreq')); - blocks.push(block('stopFreq')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { - blocks.push('-'); - blocks.push(devModeText()); - blocks.push('-'); - blocks.push(block('doPlayFrequency')); - } - }, - - pen: () => { - blocks.push(block('clear')); - blocks.push('-'); - blocks.push(block('down')); - blocks.push(block('up')); - blocks.push(watcherToggle('getPenDown')); - blocks.push(block('getPenDown', this.inheritsAttribute('pen down?'))); - blocks.push('-'); - blocks.push(block('setColor')); - blocks.push(block('changePenHSVA')); - blocks.push(block('setPenHSVA')); - blocks.push(block('getPenAttribute')); - blocks.push('-'); - blocks.push(block('changeSize')); - blocks.push(block('setSize')); - blocks.push('-'); - blocks.push(block('doStamp')); - blocks.push(block('floodFill')); - blocks.push(block('write')); - blocks.push('-'); - blocks.push(block('reportPenTrailsAsCostume')); - blocks.push('-'); - blocks.push(block('doPasteOn')); - blocks.push(block('doCutFrom')); - }, - - control: () => { - blocks.push(block('receiveGo')); - blocks.push(block('receiveKey')); - blocks.push(block('receiveInteraction')); - blocks.push(block('receiveCondition')); - blocks.push(block('receiveMessage')); - blocks.push('-'); - blocks.push(block('doBroadcast')); - blocks.push(block('doBroadcastAndWait')); - blocks.push(block('doSend')); - blocks.push(watcherToggle('getLastMessage')); - blocks.push(block('getLastMessage')); - blocks.push('-'); - blocks.push(block('doWarp')); - blocks.push('-'); - blocks.push(block('doWait')); - blocks.push(block('doWaitUntil')); - blocks.push('-'); - blocks.push(block('doForever')); - blocks.push(block('doRepeat')); - blocks.push(block('doUntil')); - blocks.push(block('doFor')); - blocks.push('-'); - blocks.push(block('doIf')); - blocks.push(block('doIfElse')); - blocks.push(block('reportIfElse')); - blocks.push('-'); - blocks.push(block('doReport')); - blocks.push(block('doStopThis')); - blocks.push('-'); - blocks.push(block('doRun')); - blocks.push(block('fork')); - blocks.push(block('evaluate')); - blocks.push('-'); - blocks.push(block('doTellTo')); - blocks.push(block('reportAskFor')); - blocks.push('-'); - blocks.push(block('doCallCC')); - blocks.push(block('reportCallCC')); - blocks.push('-'); - blocks.push(block('receiveOnClone')); - blocks.push(block('createClone')); - blocks.push(block('newClone')); - blocks.push(block('removeClone')); - blocks.push('-'); - blocks.push(block('doPauseAll')); - }, - - sensing: () => { - blocks.push(block('reportTouchingObject')); - blocks.push(block('reportTouchingColor')); - blocks.push(block('reportColorIsTouchingColor')); - blocks.push('-'); - blocks.push(block('doAsk')); - blocks.push(watcherToggle('getLastAnswer')); - blocks.push(block('getLastAnswer')); - blocks.push('-'); - blocks.push(watcherToggle('reportMouseX')); - blocks.push(block('reportMouseX')); - blocks.push(watcherToggle('reportMouseY')); - blocks.push(block('reportMouseY')); - blocks.push(block('reportMouseDown')); - blocks.push('-'); - blocks.push(block('reportKeyPressed')); - blocks.push('-'); - blocks.push(block('reportRelationTo')); - blocks.push(block('reportAspect')); - blocks.push('-'); - blocks.push(block('doResetTimer')); - blocks.push(watcherToggle('getTimer')); - blocks.push(block('getTimer')); - blocks.push('-'); - blocks.push(block('reportAttributeOf')); - - if (SpriteMorph.prototype.enableFirstClass) { - blocks.push(block('reportGet')); - } - - blocks.push(block('reportObject')); - blocks.push('-'); - blocks.push(block('reportURL')); - blocks.push(block('reportAudio')); - blocks.push(block('reportVideo')); - blocks.push(block('doSetVideoTransparency')); - blocks.push('-'); - blocks.push(block('reportGlobalFlag')); - blocks.push(block('doSetGlobalFlag')); - blocks.push('-'); - blocks.push(block('reportDate')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { - blocks.push('-'); - blocks.push(devModeText()); - blocks.push('-'); - blocks.push(watcherToggle('reportThreadCount')); - blocks.push(block('reportThreadCount')); - blocks.push(block('reportStackSize')); - blocks.push(block('reportFrameCount')); - blocks.push(block('reportYieldCount')); - } - }, - - operators: () => { - blocks.push(block('reifyScript')); - blocks.push(block('reifyReporter')); - blocks.push(block('reifyPredicate')); - blocks.push('#'); - blocks.push('-'); - blocks.push(block('reportSum')); - blocks.push(block('reportDifference')); - blocks.push(block('reportProduct')); - blocks.push(block('reportQuotient')); - blocks.push(block('reportPower')); - blocks.push('-'); - blocks.push(block('reportModulus')); - blocks.push(block('reportRound')); - blocks.push(block('reportMonadic')); - blocks.push(block('reportRandom')); - blocks.push('-'); - blocks.push(block('reportLessThan')); - blocks.push(block('reportEquals')); - blocks.push(block('reportGreaterThan')); - blocks.push('-'); - blocks.push(block('reportAnd')); - blocks.push(block('reportOr')); - blocks.push(block('reportNot')); - blocks.push(block('reportBoolean')); - blocks.push('-'); - blocks.push(block('reportJoinWords')); - blocks.push(block('reportTextSplit')); - blocks.push(block('reportLetter')); - blocks.push(block('reportStringSize')); - blocks.push('-'); - blocks.push(block('reportUnicode')); - blocks.push(block('reportUnicodeAsLetter')); - blocks.push('-'); - blocks.push(block('reportIsA')); - blocks.push(block('reportIsIdentical')); - - if (Process.prototype.enableJS) { - blocks.push('-'); - blocks.push(block('reportJSFunction')); - if (Process.prototype.enableCompiling) { - blocks.push(block('reportCompiled')); - } - } - // for debugging: /////////////// - - if (this.world().isDevMode) { - blocks.push('-'); - blocks.push(devModeText()); - blocks.push('-'); - blocks.push(block('reportTypeOf')); - blocks.push(block('reportTextFunction')); - } - }, - - variables: () => { - button = new PushButtonMorph( + button = new PushButtonMorph( + null, + function () { + new VariableDialogMorph( null, - function () { - new VariableDialogMorph( - null, - addVar, - myself - ).prompt( - 'Variable name', - null, - myself.world() - ); - }, - 'Make a variable' + addVar, + myself + ).prompt( + 'Variable name', + null, + myself.world() ); - button.userMenu = helpMenu; - button.selector = 'addVariable'; - button.showHelp = BlockMorph.prototype.showHelp; - blocks.push(button); + }, + 'Make a variable' + ); + button.userMenu = this.newHelpMenu; + button.selector = 'addVariable'; + button.showHelp = BlockMorph.prototype.showHelp; + return button; +} - if (this.deletableVariableNames().length > 0) { - button = new PushButtonMorph( +SpriteMorph.prototype.deleteVariableButton = function () { + let button, myself = this; + button = new PushButtonMorph( + null, + function () { + var menu = new MenuMorph( + myself.deleteVariable, + null, + myself + ); + myself.deletableVariableNames().forEach(name => + menu.addItem( + name, + name, null, - function () { - var menu = new MenuMorph( - myself.deleteVariable, - null, - myself - ); - myself.deletableVariableNames().forEach(name => - menu.addItem( - name, - name, - null, - null, - null, - null, - null, - null, - true // verbatim - don't translate - ) - ); - menu.popUpAtHand(myself.world()); - }, - 'Delete a variable' - ); - button.userMenu = helpMenu; - button.selector = 'deleteVariable'; - button.showHelp = BlockMorph.prototype.showHelp; - blocks.push(button); - } + null, + null, + null, + null, + null, + true // verbatim - don't translate + ) + ); + menu.popUpAtHand(myself.world()); + }, + 'Delete a variable' + ); + button.userMenu = this.newHelpMenu; + button.selector = 'deleteVariable'; + button.showHelp = BlockMorph.prototype.showHelp; + return button; +} - blocks.push('-'); +SpriteMorph.prototype.devModeText = function () { + let txt = new TextMorph(localize('development mode \ndebugging primitives:')); + txt.fontSize = 9; + txt.setColor(this.paletteTextColor); + return txt; +} - varNames = this.reachableGlobalVariableNames(true); - if (varNames.length > 0) { - varNames.forEach(name => { - blocks.push(variableWatcherToggle(name)); - blocks.push(variableBlock(name)); - }); - blocks.push('-'); - } +SpriteMorph.prototype.newHelpMenu = function () { + // return a 1 item context menu for anything that implements a 'showHelp' method. + var menu = new MenuMorph(this); + menu.addItem('help...', 'showHelp'); + return menu; +} - varNames = this.allLocalVariableNames(true); - if (varNames.length > 0) { - varNames.forEach(name => { - blocks.push(variableWatcherToggle(name)); - blocks.push(variableBlock(name, true)); - }); - blocks.push('-'); - } - - blocks.push(block('doSetVar')); - blocks.push(block('doChangeVar')); - blocks.push(block('doShowVar')); - blocks.push(block('doHideVar')); - blocks.push(block('doDeclareVariables')); - - // inheritance: - - if (StageMorph.prototype.enableInheritance) { - blocks.push('-'); - blocks.push(block('doDeleteAttr')); - } - - /////////////////////////////// - - blocks.push('='); - - blocks.push(block('reportNewList')); - blocks.push(block('reportNumbers')); - blocks.push('-'); - blocks.push(block('reportCONS')); - blocks.push(block('reportListItem')); - blocks.push(block('reportCDR')); - blocks.push('-'); - blocks.push(block('reportListAttribute')); - blocks.push(block('reportListIndex')); - blocks.push(block('reportListContainsItem')); - blocks.push(block('reportListIsEmpty')); - blocks.push('-'); - blocks.push(block('reportMap')); - blocks.push(block('reportKeep')); - blocks.push(block('reportFindFirst')); - blocks.push(block('reportCombine')); - blocks.push('-'); - blocks.push(block('doForEach')); - blocks.push('-'); - blocks.push(block('reportConcatenatedLists')); - blocks.push(block('reportReshape')); - blocks.push('-'); - blocks.push(block('doAddToList')); - blocks.push(block('doDeleteFromList')); - blocks.push(block('doInsertInList')); - blocks.push(block('doReplaceInList')); - - // for debugging: /////////////// - - if (this.world().isDevMode) { - blocks.push('-'); - blocks.push(devModeText()); - blocks.push('-'); - blocks.push(block('doShowTable')); - } - - if (StageMorph.prototype.enableCodeMapping) { - blocks.push('='); - blocks.push(block('doMapCodeOrHeader')); - blocks.push(block('doMapValueCode')); - blocks.push(block('doMapListCode')); - blocks.push('-'); - blocks.push(block('reportMappedCode')); - } - blocks.push(block('doShowTable')); - blocks.push('-'); - blocks.push(block('doApplyExtension')); - blocks.push(block('reportApplyExtension')); - } - } - - // In a Unified Palette custom blocks appear following each category, - // but there is only 1 make a block button (at the end). - if (cat === 'unified') { - for (currentCategory in paletteDefitions) { - paletteDefitions[currentCategory](); - blocks.push('='); - - } - blocks.push(this.makeBlockButton()); - } else { - paletteDefitions[cat](); - } - - return blocks; -}; - -// returns an array all custom block templates for a selected category. +// returns an array alock templates for a selected category. SpriteMorph.prototype.customBlockTemplatesForCategory = function (category) { var ide = this.parentThatIsA(IDE_Morph), blocks = [], isInherited = false, block, inheritedBlocks; @@ -2878,12 +2865,7 @@ SpriteMorph.prototype.makeBlockButton = function (category) { 'Make a block' ); - button.userMenu = function () { - var menu = new MenuMorph(this); - menu.addItem('help...', 'showHelp'); - return menu; - }; - + button.userMenu = this.newHelpMenu; button.selector = 'addCustomBlock'; button.showHelp = BlockMorph.prototype.showHelp; return button; @@ -3104,11 +3086,27 @@ SpriteMorph.prototype.freshPalette = function (category) { return menu; }; - blocks = this.getPrimitiveTemplates(category); + if (category === 'unified') { + // In a Unified Palette custom blocks appear following each category, + // but there is only 1 make a block button (at the end). + blocks = this.categories.reduce((blocks, category) => + blocks.concat( + this.getPrimitiveTemplates(category), + '=', + this.customBlockTemplatesForCategory(category), + '=' + ), + []); + } else { + blocks = this.getPrimitiveTemplates(category); + } blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - blocks.push('='); - blocks.push(...this.customBlockTemplatesForCategory(cat)); + blocks.push(this.makeBlockButton(category)); + + if (category !== 'unified') { + blocks.push('='); + blocks.push(...this.customBlockTemplatesForCategory(category)); + } blocks.forEach(block => { if (block === null) { @@ -8476,8 +8474,8 @@ StageMorph.prototype.pauseGenericHatBlocks = function () { // StageMorph block templates StageMorph.prototype.blockTemplates = function (category) { - var blocks = [], myself = this, varNames, button, - cat = category || 'motion', txt; + var blocks = [], myself = this, varNames, + category = category || 'motion', txt; function block(selector) { if (myself.hiddenPrimitives[selector]) { @@ -8534,35 +8532,14 @@ StageMorph.prototype.blockTemplates = function (category) { ); } - function addVar(pair) { - if (pair) { - var ide; - if (myself.isVariableNameInUse(pair[0])) { - myself.inform('that name is already in use'); - } else { - ide = myself.parentThatIsA(IDE_Morph); - myself.addVariable(pair[0], pair[1]); - myself.toggleVariableWatcher(pair[0], pair[1]); - myself.primitivesCache[cat] = null; - myself.paletteCache[cat] = null; - ide.refreshPalette(); - ide.recordUnsavedChanges(); - } - } - } + if (category === 'motion') { - if (cat === 'motion') { - - txt = new TextMorph(localize( - 'Stage selected:\nno motion primitives' - )); + txt = new TextMorph(localize('Stage selected:\nno motion primitives')); txt.fontSize = 9; txt.setColor(this.paletteTextColor); blocks.push(txt); - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'looks' || cat === 'unified') { + } else if (category === 'looks') { blocks.push(block('doSwitchToCostume')); blocks.push(block('doWearNextCostume')); @@ -8588,12 +8565,7 @@ StageMorph.prototype.blockTemplates = function (category) { // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(block('log')); blocks.push(block('alert')); @@ -8601,9 +8573,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doScreenshot')); } - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'sound' || cat === 'unified') { + } else if (category === 'sound') { blocks.push(block('playSound')); blocks.push(block('doPlaySoundUntilDone')); @@ -8638,19 +8608,12 @@ StageMorph.prototype.blockTemplates = function (category) { // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(block('doPlayFrequency')); } - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'pen' || cat === 'unified') { + } else if (category === 'pen') { blocks.push(block('clear')); blocks.push('-'); @@ -8663,9 +8626,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doPasteOn')); blocks.push(block('doCutFrom')); - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'control' || cat === 'unified') { + } else if (category === 'control') { blocks.push(block('receiveGo')); blocks.push(block('receiveKey')); @@ -8711,9 +8672,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('doPauseAll')); - if (cat === 'unified') {blocks.push('='); } - } - if (cat === 'sensing' || cat === 'unified') { + } else if (category === 'sensing') { blocks.push(block('doAsk')); blocks.push(watcherToggle('getLastAnswer')); @@ -8752,16 +8711,9 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportDate')); // for debugging: /////////////// - if (this.world().isDevMode) { - blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(watcherToggle('reportThreadCount')); blocks.push(block('reportThreadCount')); @@ -8769,10 +8721,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportFrameCount')); blocks.push(block('reportYieldCount')); } - - if (cat === 'unified') {blocks.push('='); } } - if (cat === 'operators' || cat === 'unified') { + if (category === 'operators') { blocks.push(block('reifyScript')); blocks.push(block('reifyReporter')); @@ -8819,70 +8769,21 @@ StageMorph.prototype.blockTemplates = function (category) { } // for debugging: /////////////// - if (this.world().isDevMode) { blocks.push('-'); - txt = new TextMorph( - 'development mode \ndebugging primitives:' - ); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(block('reportTypeOf')); blocks.push(block('reportTextFunction')); } - if (cat === 'unified') {blocks.push('='); } } - if (cat === 'variables' || cat === 'unified') { - - button = new PushButtonMorph( - null, - function () { - new VariableDialogMorph( - null, - addVar, - myself - ).prompt( - 'Variable name', - null, - myself.world() - ); - }, - 'Make a variable' - ); - blocks.push(button); + if (category === 'variables') { + blocks.push(this.makeAVariableButton()); if (this.variables.allNames().length > 0) { - button = new PushButtonMorph( - null, - function () { - var menu = new MenuMorph( - myself.deleteVariable, - null, - myself - ); - myself.variables.allNames().forEach(name => - menu.addItem( - name, - name, - null, - null, - null, - null, - null, - null, - true // verbatim - don't translate - ) - ); - menu.popUpAtHand(myself.world()); - }, - 'Delete a variable' - ); - blocks.push(button); + blocks.push(this.deleteVariableButton()); } - blocks.push('-'); varNames = this.reachableGlobalVariableNames(true); @@ -8937,15 +8838,9 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doReplaceInList')); // for debugging: /////////////// - if (this.world().isDevMode) { blocks.push('-'); - txt = new TextMorph(localize( - 'development mode \ndebugging primitives:' - )); - txt.fontSize = 9; - txt.setColor(this.paletteTextColor); - blocks.push(txt); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(block('doShowTable')); blocks.push('-'); @@ -8953,8 +8848,6 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportApplyExtension')); } - //////// ///////////////////////// - blocks.push('='); if (StageMorph.prototype.enableCodeMapping) { @@ -8965,16 +8858,22 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportMappedCode')); blocks.push('='); } - - if (cat === 'unified') {blocks.push('='); } } - blocks.push('='); - blocks.push(this.makeBlockButton(cat)); - return blocks; }; +// StageMorph Palette Utilities +StageMorph.prototype.newHelpMenu = SpriteMorph.prototype.newHelpMenu; +StageMorph.prototype.makeBlockButton = SpriteMorph.prototype.makeBlockButton; +StageMorph.prototype.makeAVariableButton = SpriteMorph.prototype.makeAVariableButton; +StageMorph.prototype.devModeText = SpriteMorph.prototype.devModeText; +StageMorph.prototype.deleteVariableButton = SpriteMorph.prototype.deleteVariableButton; +StageMorph.prototype.customBlockTemplatesForCategory = + SpriteMorph.prototype.customBlockTemplatesForCategory; +StageMorph.prototype.getPrimitiveTemplates = + SpriteMorph.prototype.getPrimitiveTemplates; + // StageMorph primitives StageMorph.prototype.clear = function () { From 2a061f57f3e365f8813224e21c1c365783044980 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 29 Jun 2021 20:41:22 -0700 Subject: [PATCH 83/92] fix cache clearing for primitive hiding --- src/blocks.js | 17 ++++++++++++++--- src/gui.js | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/blocks.js b/src/blocks.js index 3a0d7d33..657df834 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -3237,11 +3237,22 @@ BlockMorph.prototype.developersMenu = function () { return menu; }; -BlockMorph.prototype.hidePrimitive = function () { - var ide = this.parentThatIsA(IDE_Morph); +BlockMorph.prototype.hidePrimitive = function (args) { + var ide = this.parentThatIsA(IDE_Morph), + dict, + cat; if (!ide) {return; } StageMorph.prototype.hiddenPrimitives[this.selector] = true; - ide.flushBlocksCache(ide.currentCategory); + dict = { + doWarp: 'control', + reifyScript: 'operators', + reifyReporter: 'operators', + reifyPredicate: 'operators', + doDeclareVariables: 'variables' + }; + cat = dict[this.selector] || this.category; + if (cat === 'lists') {cat = 'variables'; } + ide.flushBlocksCache(cat); ide.refreshPalette(); }; diff --git a/src/gui.js b/src/gui.js index 4c6fd852..12a706cf 100644 --- a/src/gui.js +++ b/src/gui.js @@ -5791,9 +5791,11 @@ IDE_Morph.prototype.flushPaletteCache = function (category) { // if no category is specified, the whole cache gets flushed if (category) { this.stage.paletteCache[category] = null; + this.stage.paletteCache.unified = null; this.stage.children.forEach(m => { if (m instanceof SpriteMorph) { m.paletteCache[category] = null; + m.paletteCache.unified = null; } }); } else { From 17d7dd67206314d7d683500472d40cebcc55fa12 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 29 Jun 2021 22:23:06 -0700 Subject: [PATCH 84/92] tidy up some code, undo some improper git merging --- src/blocks.js | 23 +++++++++++---------- src/gui.js | 54 ++++++++++++++++++-------------------------------- src/objects.js | 53 +++++++++++++++++++++++-------------------------- 3 files changed, 57 insertions(+), 73 deletions(-) diff --git a/src/blocks.js b/src/blocks.js index 657df834..3450de16 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -7200,7 +7200,7 @@ ScriptsMorph.prototype.userMenu = function () { } else { obj.customBlocks.push(definition); } - ide.flushBlocksCache(); + ide.flushPaletteCache(); ide.refreshPalette(); new BlockEditorMorph(definition, obj).popUp(); } @@ -9776,16 +9776,19 @@ InputSlotMorph.prototype.audioMenu = function (searching) { }; InputSlotMorph.prototype.scenesMenu = function (searching) { - var scenes = this.parentThatIsA(IDE_Morph).scenes, - dict = {}; - - if (!searching && scenes.length() > 1) { - scenes.itemsArray().forEach(scn => { - if (scn.name) { - dict[scn.name] = scn.name; - } - }); + var dict = {}, + scenes; + if (!searching) { + scenes = this.parentThatIsA(IDE_Morph).scenes; + if (scenes.length() > 1) { + scenes.itemsArray().forEach(scn => { + if (scn.name) { + dict[scn.name] = scn.name; + } + }); + } } + dict['~'] = null; dict.next = ['next']; dict.previous = ['previous']; diff --git a/src/gui.js b/src/gui.js index 12a706cf..da068456 100644 --- a/src/gui.js +++ b/src/gui.js @@ -2049,29 +2049,6 @@ IDE_Morph.prototype.createCorral = function (keepSceneAlbum) { }; }; -IDE_Morph.prototype.unsetUnifiedPalete = function () { - this.scene.unifiedPalette = false; - this.currentCategory = 'motion'; - this.createCategories(); - this.categories.fixLayout(); - this.fixLayout(); - this.flushBlocksCache(); - this.currentSprite.palette(this.currentCategory); - this.refreshPalette(true); -} - -IDE_Morph.prototype.setUnifiedPalete = function () { - this.scene.unifiedPalette = true; - this.currentCategory = 'unified'; - this.createCategories(); - this.categories.fixLayout(); - this.fixLayout(); - this.flushBlocksCache(); - this.flushPaletteCache(); - this.currentSprite.palette(this.currentCategory); - this.refreshPalette(true); -} - // IDE_Morph layout IDE_Morph.prototype.fixLayout = function (situation) { @@ -4050,13 +4027,7 @@ IDE_Morph.prototype.settingsMenu = function () { ); addPreference( 'Unified Palette', - () => { - if (this.scene.unifiedPalette) { - this.unsetUnifiedPalete(); - } else { - this.setUnifiedPalete(); - } - }, + () => this.toggleUnifiedPalette(), this.scene.unifiedPalette, 'uncheck to show only the selected category\'s blocks', 'check to show all blocks in a single palette', @@ -5602,9 +5573,9 @@ IDE_Morph.prototype.switchToScene = function (scene, refreshAlbum) { this.world().keyboardFocus = this.stage; if (this.currentCategory != 'unified' && scene.unifiedPalette) { - this.setUnifiedPalete(); + this.toggleUnifiedPalette(); } else if (this.currentCategory == 'unified' && !scene.unifiedPalette) { - this.unsetUnifiedPalete(); + this.toggleUnifiedPalette(); } this.fixLayout(); @@ -5766,14 +5737,11 @@ IDE_Morph.prototype.switchToDevMode = function () { IDE_Morph.prototype.flushBlocksCache = function (category) { // if no category is specified, the whole cache gets flushed - // the 'unified' category is always flushed. if (category) { this.stage.primitivesCache[category] = null; - this.stage.primitivesCache.unified = null; this.stage.children.forEach(m => { if (m instanceof SpriteMorph) { m.primitivesCache[category] = null; - m.primitivesCache.unified = null; } }); } else { @@ -6049,6 +6017,22 @@ IDE_Morph.prototype.toggleStageSize = function (isSmall, forcedRatio) { } }; +IDE_Morph.prototype.toggleUnifiedPalette = function () { + this.scene.unifiedPalette = !this.scene.unifiedPalette; + if (this.scene.unifiedPalette) { + this.currentCategory = 'unified'; + } else { + this.currentCategory = 'motion'; + } + + this.createCategories(); + this.categories.fixLayout(); + this.fixLayout(); + this.flushBlocksCache(); + this.currentSprite.palette(this.currentCategory); + this.refreshPalette(true); +} + IDE_Morph.prototype.setPaletteWidth = function (newWidth) { var msecs = this.isAnimating ? 100 : 0, world = this.world(); diff --git a/src/objects.js b/src/objects.js index 9a257b7d..f0201df3 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2637,7 +2637,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); - blocks.push(devModeText()); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(block('reportTypeOf')); blocks.push(block('reportTextFunction')); @@ -2645,7 +2645,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { } else if (category === 'variables') { - blocks.push(this.makeAVariableButton()); + blocks.push(this.makeVariableButton()); if (this.deletableVariableNames().length > 0) { blocks.push(this.deleteVariableButton()); } @@ -2713,7 +2713,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); - blocks.push(devModeText()); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(block('doShowTable')); blocks.push('-'); @@ -2735,7 +2735,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { }; // Utitlies displayed in the palette -SpriteMorph.prototype.makeAVariableButton = function() { +SpriteMorph.prototype.makeVariableButton = function () { let button, myself = this; function addVar(pair) { @@ -2769,7 +2769,7 @@ SpriteMorph.prototype.makeAVariableButton = function() { }, 'Make a variable' ); - button.userMenu = this.newHelpMenu; + button.userMenu = this.helpMenu; button.selector = 'addVariable'; button.showHelp = BlockMorph.prototype.showHelp; return button; @@ -2802,7 +2802,7 @@ SpriteMorph.prototype.deleteVariableButton = function () { }, 'Delete a variable' ); - button.userMenu = this.newHelpMenu; + button.userMenu = this.helpMenu; button.selector = 'deleteVariable'; button.showHelp = BlockMorph.prototype.showHelp; return button; @@ -2815,7 +2815,7 @@ SpriteMorph.prototype.devModeText = function () { return txt; } -SpriteMorph.prototype.newHelpMenu = function () { +SpriteMorph.prototype.helpMenu = function () { // return a 1 item context menu for anything that implements a 'showHelp' method. var menu = new MenuMorph(this); menu.addItem('help...', 'showHelp'); @@ -2865,7 +2865,7 @@ SpriteMorph.prototype.makeBlockButton = function (category) { 'Make a block' ); - button.userMenu = this.newHelpMenu; + button.userMenu = this.helpMenu; button.selector = 'addCustomBlock'; button.showHelp = BlockMorph.prototype.showHelp; return button; @@ -2887,7 +2887,7 @@ SpriteMorph.prototype.makeBlock = function () { } else { this.customBlocks.push(definition); } - ide.flushBlocksCache(); + ide.flushPaletteCache(); ide.refreshPalette(); ide.recordUnsavedChanges(); new BlockEditorMorph(definition, this).popUp(); @@ -8780,7 +8780,7 @@ StageMorph.prototype.blockTemplates = function (category) { } if (category === 'variables') { - blocks.push(this.makeAVariableButton()); + blocks.push(this.makeVariableButton()); if (this.variables.allNames().length > 0) { blocks.push(this.deleteVariableButton()); } @@ -8856,24 +8856,12 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('doMapListCode')); blocks.push('-'); blocks.push(block('reportMappedCode')); - blocks.push('='); } } return blocks; }; -// StageMorph Palette Utilities -StageMorph.prototype.newHelpMenu = SpriteMorph.prototype.newHelpMenu; -StageMorph.prototype.makeBlockButton = SpriteMorph.prototype.makeBlockButton; -StageMorph.prototype.makeAVariableButton = SpriteMorph.prototype.makeAVariableButton; -StageMorph.prototype.devModeText = SpriteMorph.prototype.devModeText; -StageMorph.prototype.deleteVariableButton = SpriteMorph.prototype.deleteVariableButton; -StageMorph.prototype.customBlockTemplatesForCategory = - SpriteMorph.prototype.customBlockTemplatesForCategory; -StageMorph.prototype.getPrimitiveTemplates = - SpriteMorph.prototype.getPrimitiveTemplates; - // StageMorph primitives StageMorph.prototype.clear = function () { @@ -9151,18 +9139,27 @@ StageMorph.prototype.categories = SpriteMorph.prototype.categories; StageMorph.prototype.blockColor = SpriteMorph.prototype.blockColor; StageMorph.prototype.paletteColor = SpriteMorph.prototype.paletteColor; StageMorph.prototype.setName = SpriteMorph.prototype.setName; -StageMorph.prototype.makeBlockButton = SpriteMorph.prototype.makeBlockButton; -StageMorph.prototype.makeBlock = SpriteMorph.prototype.makeBlock; -StageMorph.prototype.palette = SpriteMorph.prototype.palette; -StageMorph.prototype.freshPalette = SpriteMorph.prototype.freshPalette; -StageMorph.prototype.blocksMatching = SpriteMorph.prototype.blocksMatching; -StageMorph.prototype.searchBlocks = SpriteMorph.prototype.searchBlocks; StageMorph.prototype.reporterize = SpriteMorph.prototype.reporterize; StageMorph.prototype.variableBlock = SpriteMorph.prototype.variableBlock; StageMorph.prototype.showingWatcher = SpriteMorph.prototype.showingWatcher; StageMorph.prototype.addVariable = SpriteMorph.prototype.addVariable; StageMorph.prototype.deleteVariable = SpriteMorph.prototype.deleteVariable; +// StageMorph Palette Utilities + +StageMorph.prototype.makeBlock = SpriteMorph.prototype.makeBlock; +StageMorph.prototype.helpMenu = SpriteMorph.prototype.helpMenu; +StageMorph.prototype.makeBlockButton = SpriteMorph.prototype.makeBlockButton; +StageMorph.prototype.makeVariableButton = SpriteMorph.prototype.makeVariableButton; +StageMorph.prototype.devModeText = SpriteMorph.prototype.devModeText; +StageMorph.prototype.deleteVariableButton = SpriteMorph.prototype.deleteVariableButton; +StageMorph.prototype.customBlockTemplatesForCategory = SpriteMorph.prototype.customBlockTemplatesForCategory; +StageMorph.prototype.getPrimitiveTemplates = SpriteMorph.prototype.getPrimitiveTemplates; +StageMorph.prototype.palette = SpriteMorph.prototype.palette; +StageMorph.prototype.freshPalette = SpriteMorph.prototype.freshPalette; +StageMorph.prototype.blocksMatching = SpriteMorph.prototype.blocksMatching; +StageMorph.prototype.searchBlocks = SpriteMorph.prototype.searchBlocks; + // StageMorph neighbor detection StageMorph.prototype.neighbors = SpriteMorph.prototype.neighbors; From 32aac68abd1b603c6bf01094a8d884e0b8ebd011 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 29 Jun 2021 22:23:57 -0700 Subject: [PATCH 85/92] remove debug arg --- src/blocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks.js b/src/blocks.js index 3450de16..ca8d0f4d 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -3237,7 +3237,7 @@ BlockMorph.prototype.developersMenu = function () { return menu; }; -BlockMorph.prototype.hidePrimitive = function (args) { +BlockMorph.prototype.hidePrimitive = function () { var ide = this.parentThatIsA(IDE_Morph), dict, cat; From 772da1fd94f3925ee454abf663293318adf15985 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Tue, 29 Jun 2021 22:25:49 -0700 Subject: [PATCH 86/92] undo spacing from bad merge --- src/blocks.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/blocks.js b/src/blocks.js index ca8d0f4d..392babd1 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -9777,18 +9777,17 @@ InputSlotMorph.prototype.audioMenu = function (searching) { InputSlotMorph.prototype.scenesMenu = function (searching) { var dict = {}, - scenes; - if (!searching) { - scenes = this.parentThatIsA(IDE_Morph).scenes; - if (scenes.length() > 1) { - scenes.itemsArray().forEach(scn => { - if (scn.name) { - dict[scn.name] = scn.name; - } - }); - } + scenes; + if (!searching) { + scenes = this.parentThatIsA(IDE_Morph).scenes; + if (scenes.length() > 1) { + scenes.itemsArray().forEach(scn => { + if (scn.name) { + dict[scn.name] = scn.name; + } + }); + } } - dict['~'] = null; dict.next = ['next']; dict.previous = ['previous']; From e5be75bb5686cde4fc8f915515aff4227bf61e07 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Wed, 30 Jun 2021 02:57:53 -0700 Subject: [PATCH 87/92] resolve bug with palette rendering polluting the primitiveCache --- src/objects.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/objects.js b/src/objects.js index f0201df3..a40a0a9d 100644 --- a/src/objects.js +++ b/src/objects.js @@ -3098,7 +3098,8 @@ SpriteMorph.prototype.freshPalette = function (category) { ), []); } else { - blocks = this.getPrimitiveTemplates(category); + // ensure we do not modify the cached array + blocks = this.getPrimitiveTemplates(category).slice(); } blocks.push('='); blocks.push(this.makeBlockButton(category)); From 125eb1b4fa1bb514ecc5b81b57c42726ee50e96b Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Wed, 30 Jun 2021 03:08:14 -0700 Subject: [PATCH 88/92] resolve issues with duplicate blocks in the variables palette --- src/objects.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/objects.js b/src/objects.js index a40a0a9d..49e411a1 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2828,9 +2828,8 @@ SpriteMorph.prototype.customBlockTemplatesForCategory = function (category) { isInherited = false, block, inheritedBlocks; function addCustomBlock(definition) { - if (category === 'unified' || definition.category === category || - (category === 'variables' - && contains(['lists', 'other'], definition.category))) { + if (definition.category === category || + (Array.isArray(category) && category.includes(definition.category))) { block = definition.templateInstance(); if (isInherited) {block.ghost(); } blocks.push(block); @@ -3104,6 +3103,10 @@ SpriteMorph.prototype.freshPalette = function (category) { blocks.push('='); blocks.push(this.makeBlockButton(category)); + if (category === 'variables') { + category = ['variables', 'lists', 'other']; + } + if (category !== 'unified') { blocks.push('='); blocks.push(...this.customBlockTemplatesForCategory(category)); From 16be129408ed4932c77905dba6b2329731c2affc Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Thu, 1 Jul 2021 14:52:37 -0700 Subject: [PATCH 89/92] Simplify palette scrollto function, update scrollbars --- src/gui.js | 17 +++++------------ src/objects.js | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/gui.js b/src/gui.js index da068456..895fa9c6 100644 --- a/src/gui.js +++ b/src/gui.js @@ -2532,18 +2532,11 @@ IDE_Morph.prototype.refreshPalette = function (shouldIgnorePosition) { }; IDE_Morph.prototype.scrollPaletteToCategory = function (category) { - var topOfCategory, - palette = this.palette.contents; - // pallete scroll top - block top + palette actual top + palette padding - palette.children.some(block => { - if (block.category == category) { - topOfCategory = block; - return true; - } - }); - palette.setTop( - palette.top() - topOfCategory.top() + this.palette.top() + this.palette.padding - ); + let palette = this.palette, + firstInCategory = palette.contents.children.find(block => block.category == category); + + palette.scrollY(palette.top() - firstInCategory.top() + palette.padding); + palette.adjustScrollBars(); } IDE_Morph.prototype.pressStart = function () { diff --git a/src/objects.js b/src/objects.js index 49e411a1..0094a27d 100644 --- a/src/objects.js +++ b/src/objects.js @@ -2961,7 +2961,7 @@ SpriteMorph.prototype.freshPalette = function (category) { searchButton.edge = 0; searchButton.padding = 3; searchButton.fixLayout(); - palette.toolBar.add(searchButton); + palette.toolBar.add(searchButton); makeButton = new PushButtonMorph( this, From ca1c20e1e0676e927990f188abc5546a3d4f97d6 Mon Sep 17 00:00:00 2001 From: Michael Ball Date: Thu, 1 Jul 2021 15:15:52 -0700 Subject: [PATCH 90/92] Maintain palette scroll position when cancelling searching blocks --- src/objects.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/objects.js b/src/objects.js index 0094a27d..74101287 100644 --- a/src/objects.js +++ b/src/objects.js @@ -3286,6 +3286,7 @@ SpriteMorph.prototype.searchBlocks = function ( var myself = this, unit = SyntaxElementMorph.prototype.fontSize, ide = this.parentThatIsA(IDE_Morph), + oldTop = ide.palette.contents.top(), oldSearch = '', searchBar = new InputFieldMorph(searchString || ''), searchPane = ide.createPalette('forSearch'), @@ -3393,6 +3394,7 @@ SpriteMorph.prototype.searchBlocks = function ( searchBar.cancel = function () { ide.refreshPalette(); + ide.palette.contents.setTop(oldTop); ide.palette.adjustScrollBars(); }; From 96bf69b83e8d1c33af69de93c94c7367e8422a69 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 2 Jul 2021 17:04:56 +0200 Subject: [PATCH 91/92] little reformattings, mostly to shut up LINT --- HISTORY.md | 4 +++ snap.html | 10 +++---- src/blocks.js | 5 ++-- src/gui.js | 12 +++++---- src/objects.js | 72 ++++++++++++++++++++++++++++++-------------------- src/scenes.js | 2 +- src/store.js | 2 +- 7 files changed, 65 insertions(+), 42 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 07c052d8..407a807e 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,11 +3,15 @@ ## in development for v7: * **New Features:** * Scenes + * unified blocks palette option, thanks, Michael! * **Notable Changes:** * saved projects remember the last edited srpite * **Notable Fixes:** * made scrollbars in the wardrobe and jukebox more responsive +### 2021-07-02 +* gui, object, store, etc.: unified blocks palette option, thanks, Michael! + ### 2021-05-21 * gui, scenes, store: proxied thumbnail, name and notes in project, restored in XML * gui: distinguished project name from scene names, removed hidden "export as plain text" option diff --git a/snap.html b/snap.html index d21c0518..7da2dd31 100755 --- a/snap.html +++ b/snap.html @@ -8,11 +8,11 @@ - + - - - + + + @@ -22,7 +22,7 @@ - + diff --git a/src/blocks.js b/src/blocks.js index 392babd1..259a1702 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -158,7 +158,7 @@ CustomCommandBlockMorph, ToggleButtonMorph, DialMorph, SnapExtensions*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2021-June-18'; +modules.blocks = '2021-July-02'; var SyntaxElementMorph; var BlockMorph; @@ -11030,7 +11030,8 @@ BooleanSlotMorph.prototype.drawKnob = function (ctx, progress) { var w = this.width(), r = this.height() / 2, shift = this.edge / 2, - slideStep = (this.width() - this.height()) / 4 * Math.max(0, (progress || 0)), + slideStep = (this.width() - this.height()) / 4 * + Math.max(0, (progress || 0)), gradient, x, y = r, diff --git a/src/gui.js b/src/gui.js index 895fa9c6..338e1d35 100644 --- a/src/gui.js +++ b/src/gui.js @@ -83,7 +83,7 @@ Animation, BoxMorph, BlockDialogMorph, Project, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2021-June-23'; +modules.gui = '2021-July-02'; // Declarations @@ -1270,7 +1270,7 @@ IDE_Morph.prototype.createCategories = function () { each.refresh() ); myself.refreshPalette(true); - } + }; } function scrollToCategory(category) { @@ -2533,11 +2533,13 @@ IDE_Morph.prototype.refreshPalette = function (shouldIgnorePosition) { IDE_Morph.prototype.scrollPaletteToCategory = function (category) { let palette = this.palette, - firstInCategory = palette.contents.children.find(block => block.category == category); + firstInCategory = palette.contents.children.find( + block => block.category === category + ); palette.scrollY(palette.top() - firstInCategory.top() + palette.padding); palette.adjustScrollBars(); -} +}; IDE_Morph.prototype.pressStart = function () { if (this.world().currentKey === 16) { // shiftClicked @@ -6024,7 +6026,7 @@ IDE_Morph.prototype.toggleUnifiedPalette = function () { this.flushBlocksCache(); this.currentSprite.palette(this.currentCategory); this.refreshPalette(true); -} +}; IDE_Morph.prototype.setPaletteWidth = function (newWidth) { var msecs = this.isAnimating ? 100 : 0, diff --git a/src/objects.js b/src/objects.js index 74101287..c12703bb 100644 --- a/src/objects.js +++ b/src/objects.js @@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph, localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph, AlignmentMorph, Process, WorldMap, copyCanvas, useBlurredShadows*/ -modules.objects = '2021-June-14'; +modules.objects = '2021-July-02'; var SpriteMorph; var StageMorph; @@ -856,7 +856,7 @@ SpriteMorph.prototype.initBlocks = function () { doTellTo: { type: 'command', category: 'control', - // spec: 'tell %spr to %cl' // I liked this version much better, -Jens + // spec: 'tell %spr to %cl' // I liked this version better, -Jens spec: 'tell %spr to %cmdRing %inputs' }, reportAskFor: { @@ -2285,9 +2285,9 @@ SpriteMorph.prototype.variableBlock = function (varName, isLocalTemplate) { // SpriteMorph block templates -SpriteMorph.prototype.blockTemplates = function (category) { +SpriteMorph.prototype.blockTemplates = function (category = 'motion') { var blocks = [], myself = this, varNames, - category = category || 'motion'; + inheritedVars = this.inheritedVariableNames(); function block(selector, isGhosted) { if (StageMorph.prototype.hiddenPrimitives[selector]) { @@ -2456,7 +2456,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { // for debugging: /////////////// if (this.world().isDevMode) { blocks.push('-'); - blocks.push(devModeText()); + blocks.push(this.devModeText()); blocks.push('-'); blocks.push(block('doPlayFrequency')); } @@ -2736,7 +2736,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { // Utitlies displayed in the palette SpriteMorph.prototype.makeVariableButton = function () { - let button, myself = this; + var button, myself = this; function addVar(pair) { var ide; @@ -2773,10 +2773,10 @@ SpriteMorph.prototype.makeVariableButton = function () { button.selector = 'addVariable'; button.showHelp = BlockMorph.prototype.showHelp; return button; -} +}; SpriteMorph.prototype.deleteVariableButton = function () { - let button, myself = this; + var button, myself = this; button = new PushButtonMorph( null, function () { @@ -2806,21 +2806,24 @@ SpriteMorph.prototype.deleteVariableButton = function () { button.selector = 'deleteVariable'; button.showHelp = BlockMorph.prototype.showHelp; return button; -} +}; SpriteMorph.prototype.devModeText = function () { - let txt = new TextMorph(localize('development mode \ndebugging primitives:')); + var txt = new TextMorph( + localize('development mode \ndebugging primitives:') + ); txt.fontSize = 9; txt.setColor(this.paletteTextColor); return txt; -} +}; SpriteMorph.prototype.helpMenu = function () { - // return a 1 item context menu for anything that implements a 'showHelp' method. + // return a 1 item context menu for anything that implements + // a 'showHelp' method. var menu = new MenuMorph(this); menu.addItem('help...', 'showHelp'); return menu; -} +}; // returns an array alock templates for a selected category. SpriteMorph.prototype.customBlockTemplatesForCategory = function (category) { @@ -2829,7 +2832,8 @@ SpriteMorph.prototype.customBlockTemplatesForCategory = function (category) { function addCustomBlock(definition) { if (definition.category === category || - (Array.isArray(category) && category.includes(definition.category))) { + (Array.isArray(category) && category.includes(definition.category)) + ) { block = definition.templateInstance(); if (isInherited) {block.ghost(); } blocks.push(block); @@ -2848,7 +2852,9 @@ SpriteMorph.prototype.customBlockTemplatesForCategory = function (category) { // inherited custom blocks: if (this.exemplar) { inheritedBlocks = this.inheritedBlocks(true); - if (this.customBlocks.length && inheritedBlocks.length) {blocks.push('='); } + if (this.customBlocks.length && inheritedBlocks.length) { + blocks.push('='); + } isInherited = true; inheritedBlocks.forEach(addCustomBlock); } @@ -2910,7 +2916,7 @@ SpriteMorph.prototype.makeBlock = function () { }; SpriteMorph.prototype.getPrimitiveTemplates = function (category) { - blocks = this.primitivesCache[category]; + var blocks = this.primitivesCache[category]; if (!blocks) { blocks = this.blockTemplates(category); if (this.isCachingPrimitives) { @@ -2918,7 +2924,7 @@ SpriteMorph.prototype.getPrimitiveTemplates = function (category) { } } return blocks; -} +}; SpriteMorph.prototype.palette = function (category) { if (!this.paletteCache[category]) { @@ -2935,7 +2941,6 @@ SpriteMorph.prototype.freshPalette = function (category) { ry = 0, blocks, hideNextSpace = false, - stage = this.parentThatIsA(StageMorph), shade = new Color(140, 140, 140), searchButton, makeButton; @@ -4325,7 +4330,7 @@ SpriteMorph.prototype.setColorComponentHSVA = function (idx, num) { idx = +idx; if (idx < 0 || idx > 3) {return; } - if (idx == 0) { + if (idx === 0) { if (n < 0 || n > 100) { // wrap the hue n = (n < 0 ? 100 : 0) + n % 100; } @@ -8479,9 +8484,8 @@ StageMorph.prototype.pauseGenericHatBlocks = function () { // StageMorph block templates -StageMorph.prototype.blockTemplates = function (category) { - var blocks = [], myself = this, varNames, - category = category || 'motion', txt; +StageMorph.prototype.blockTemplates = function (category = 'motion') { + var blocks = [], myself = this, varNames, txt; function block(selector) { if (myself.hiddenPrimitives[selector]) { @@ -8989,7 +8993,9 @@ StageMorph.prototype.fancyThumbnail = function ( ctx.restore(); } this.children.forEach(morph => { - if ((isSnapObject(morph) || !noWatchers) && morph.isVisible && (morph !== excludedSprite)) { + if ((isSnapObject(morph) || !noWatchers) && + morph.isVisible && (morph !== excludedSprite) + ) { fb = morph.fullBounds(); fimg = morph.fullImage(); if (fimg.width && fimg.height) { @@ -9100,7 +9106,7 @@ StageMorph.prototype.setColorComponentHSVA = function (idx, num) { idx = +idx; if (idx < 0 || idx > 3) {return; } - if (idx == 0) { + if (idx === 0) { if (n < 0 || n > 100) { // wrap the hue n = (n < 0 ? 100 : 0) + n % 100; } @@ -9156,11 +9162,21 @@ StageMorph.prototype.deleteVariable = SpriteMorph.prototype.deleteVariable; StageMorph.prototype.makeBlock = SpriteMorph.prototype.makeBlock; StageMorph.prototype.helpMenu = SpriteMorph.prototype.helpMenu; StageMorph.prototype.makeBlockButton = SpriteMorph.prototype.makeBlockButton; -StageMorph.prototype.makeVariableButton = SpriteMorph.prototype.makeVariableButton; + +StageMorph.prototype.makeVariableButton + = SpriteMorph.prototype.makeVariableButton; + StageMorph.prototype.devModeText = SpriteMorph.prototype.devModeText; -StageMorph.prototype.deleteVariableButton = SpriteMorph.prototype.deleteVariableButton; -StageMorph.prototype.customBlockTemplatesForCategory = SpriteMorph.prototype.customBlockTemplatesForCategory; -StageMorph.prototype.getPrimitiveTemplates = SpriteMorph.prototype.getPrimitiveTemplates; + +StageMorph.prototype.deleteVariableButton + = SpriteMorph.prototype.deleteVariableButton; + +StageMorph.prototype.customBlockTemplatesForCategory + = SpriteMorph.prototype.customBlockTemplatesForCategory; + +StageMorph.prototype.getPrimitiveTemplates + = SpriteMorph.prototype.getPrimitiveTemplates; + StageMorph.prototype.palette = SpriteMorph.prototype.palette; StageMorph.prototype.freshPalette = SpriteMorph.prototype.freshPalette; StageMorph.prototype.blocksMatching = SpriteMorph.prototype.blocksMatching; diff --git a/src/scenes.js b/src/scenes.js index 95d38205..5cced175 100644 --- a/src/scenes.js +++ b/src/scenes.js @@ -51,7 +51,7 @@ /*global modules, VariableFrame, StageMorph, SpriteMorph, Process, List, normalizeCanvas, SnapSerializer*/ -modules.scenes = '2021-May-21'; +modules.scenes = '2021-July-02'; // Projecct ///////////////////////////////////////////////////////// diff --git a/src/store.js b/src/store.js index 165cc379..6002ec33 100644 --- a/src/store.js +++ b/src/store.js @@ -61,7 +61,7 @@ Project*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2021-June-24'; +modules.store = '2021-July-02'; // XML_Serializer /////////////////////////////////////////////////////// /* From b82ea9dae5e3ff172393168f220d12372c7c0ae0 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Fri, 2 Jul 2021 17:09:05 +0200 Subject: [PATCH 92/92] changed spelling of palette --- src/gui.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui.js b/src/gui.js index 338e1d35..4e1a0f28 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1260,10 +1260,10 @@ IDE_Morph.prototype.createCategories = function () { if (this.scene.unifiedPalette) { categorySelectionAction = scrollToCategory; } else { - categorySelectionAction = changePallete; + categorySelectionAction = changePalette; } - function changePallete(category) { + function changePalette(category) { return () => { myself.currentCategory = category; myself.categories.children.forEach(each =>