Merge pull request #2855 from jmoenig/scenes

Scenes
snap7
Jens Mönig 2021-07-02 17:10:25 +02:00 zatwierdzone przez GitHub
commit de69bf77e5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
11 zmienionych plików z 1948 dodań i 968 usunięć

Wyświetl plik

@ -1,5 +1,99 @@
# Snap! (BYOB) History
## 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
* gui: sceneified project notes
* 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
### 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
* 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
* 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: ensured unique scene names
### 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"
### 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()
* 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
* gui, scenes: migrated "new project" feature
* gui: replaced openScene() with openProject()
### 2021-04-21
* store, gui: refactored project loading structure
### 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
* scenes, store, gui: export multi-scene projects
### 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
* morphic, gui: support bulk-file-drop for importing scenes
* gui: tweaked scene album colors
### 2021-04-08
* gui: scroll selected scene icon into view
### 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
## in development:
* **New Features:**
@ -187,6 +281,23 @@
* fixed DEAL in the APL library, thanks, Brian!
* objects: fixed a resizing edge case bug for the stage prompter (ASK command)
### 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
* gui: turned scenes into an observable list
* gui: added scene icon thumbnails
### 2021-03-25
* gui, scenes: sceneified trash
* gui: first "live" multi-scene experiment
### 2021-03-19
* gui, store, scenes: capture global settings in scenes
## 6.7.3
* **Notable Changes:**
* hyperized "key _ pressed?" predicate
@ -201,7 +312,11 @@
* threads: hyperized "key _ pressed?" predicate
* prepared patch
### 2021-03-18
* gui, scenes, objects: more scene-refactorings
### 2021-03-17
* objects, gui, paint, sketch, store: de-globalized stage dimensions
* new dev version
* threads fixed repeat for non-numbers, thanks Stefan!
* updated list-utilities library, thanks, Brian!
@ -215,6 +330,15 @@
* Catalan, thanks, Joan!
### 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
* Catalan translation update, thanks, Joan!
* lists, apl: fixed "transpose", thanks, Brian!

Wyświetl plik

@ -3,25 +3,26 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Snap! 6.10.0 - dev - Build Your Own Blocks</title>
<title>Snap! 7 - dev - Build Your Own Blocks</title>
<link rel="icon" href="src/favicon.ico">
<script src="src/morphic.js?version=2021-02-10"></script>
<script src="src/morphic.js?version=2021-04-12"></script>
<script src="src/symbols.js?version=2021-03-03"></script>
<script src="src/widgets.js?version=2021-01-05"></script>
<script src="src/blocks.js?version=2021-06-18"></script>
<script src="src/blocks.js?version=2021-07-02"></script>
<script src="src/threads.js?version=2021-06-24"></script>
<script src="src/objects.js?version=2021-06-14"></script>
<script src="src/gui.js?version=2021-06-23"></script>
<script src="src/objects.js?version=2021-07-02"></script>
<script src="src/scenes.js?version=2021-07-02"></script>
<script src="src/gui.js?version=2021-07-02"></script>
<script src="src/paint.js?version=2020-05-17"></script>
<script src="src/lists.js?version=2021-03-15"></script>
<script src="src/byob.js?version=2021-06-24"></script>
<script src="src/tables.js?version=2021-03-05"></script>
<script src="src/sketch.js?version=2020-07-13"></script>
<script src="src/sketch.js?version=2021-03-17"></script>
<script src="src/video.js?version=2019-06-27"></script>
<script src="src/maps.js?version=2021-06-15"></script>
<script src="src/extensions.js?version=2021-07-02"></script>
<script src="src/xml.js?version=2020-04-27"></script>
<script src="src/store.js?version=2021-06-24"></script>
<script src="src/store.js?version=2021-07-02"></script>
<script src="src/locale.js?version=2021-06-11"></script>
<script src="src/cloud.js?version=2021-02-04"></script>
<script src="src/api.js?version=2021-01-25"></script>

Wyświetl plik

@ -158,7 +158,7 @@ CustomCommandBlockMorph, ToggleButtonMorph, DialMorph, SnapExtensions*/
// Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2021-June-18';
modules.blocks = '2021-July-02';
var SyntaxElementMorph;
var BlockMorph;
@ -753,6 +753,11 @@ SyntaxElementMorph.prototype.labelParts = {
'parameters' : ['parameters']
}
},
'%scn': {
type: 'input',
tags: 'read-only',
menu: 'scenesMenu'
},
// video
@ -926,7 +931,7 @@ SyntaxElementMorph.prototype.labelParts = {
type: 'ring slot'
tags: 'static',
kind: 'command', 'reporter', 'predicate'
*/
'%rc': {
type: 'ring slot',
@ -2052,7 +2057,7 @@ SyntaxElementMorph.prototype.fixLayout = function () {
return;
}
}
this.fixHighlight();
};
@ -2460,6 +2465,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:
@ -4246,7 +4252,7 @@ BlockMorph.prototype.render = function (ctx) {
this.outlinePath(ctx, 0);
ctx.closePath();
ctx.fill();
// add 3D-Effect:
this.drawEdges(ctx);
}
@ -5882,11 +5888,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
};
@ -6577,7 +6583,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);
}
@ -9759,7 +9765,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;
@ -9769,6 +9775,28 @@ InputSlotMorph.prototype.audioMenu = function (searching) {
return dict;
};
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;
}
});
}
}
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
@ -11002,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,
@ -14272,7 +14301,7 @@ ScriptFocusMorph.prototype.reactToKeyEvent = function (key) {
cmd = new CommandBlockMorph();
cmd.setSpec('command %cmdRing');
rings = new CommandBlockMorph();
rings.setSpec('reporter %repRing predicate %predRing');

1196
src/gui.js

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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
-------------------
@ -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:

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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();

211
src/scenes.js 100644
Wyświetl plik

@ -0,0 +1,211 @@
/*
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 <http://www.gnu.org/licenses/>.
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:
Project
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, StageMorph, SpriteMorph, Process, List,
normalizeCanvas, SnapSerializer*/
modules.scenes = '2021-July-02';
// 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, current) {
var projectScene;
this.scenes = scenes || new List();
this.currentScene = current;
// proxied for display
this.name = null;
this.notes = null;
this.thumbnail = null;
projectScene = this.scenes.at(1);
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;
// 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();
this.scenes.add(scene);
};
// 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 = '';
this.notes = '';
this.globalVariables = 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();
this.currentSprite = null;
// 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.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(
this.stage.center().subtract(
sprite.extent().divideBy(2)
)
);
this.stage.add(sprite);
this.sprites.add(sprite);
this.currentSprite = sprite;
return sprite;
};
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;
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.unifiedPalette = this.unifiedPalette;
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;
};
Scene.prototype.updateTrash = function () {
this.trash = this.trash.filter(sprite => sprite.isCorpse);
};

Wyświetl plik

@ -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();

Wyświetl plik

@ -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,20 +49,19 @@
*/
/*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,
Project*/
// Global stuff ////////////////////////////////////////////////////////
modules.store = '2021-June-24';
modules.store = '2021-July-02';
// XML_Serializer ///////////////////////////////////////////////////////
/*
@ -78,6 +77,7 @@ modules.store = '2021-June-24';
function XML_Serializer() {
this.contents = [];
this.media = [];
this.root = {};
this.isCollectingMedia = false;
this.isExportingBlocksLibrary = false;
}
@ -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:
@ -109,6 +109,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 +177,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 () {
@ -247,7 +251,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);
@ -279,7 +283,7 @@ function SnapSerializer() {
// SnapSerializer initialization:
SnapSerializer.prototype.init = function () {
this.project = {};
this.scene = new Scene();
this.objects = {};
this.mediaDict = {};
};
@ -316,7 +320,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,18 +332,28 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide, remixID) {
'\n\nand may be incompatible or fail to load here.'
);
}
return this.rawLoadProjectModel(xmlNode, 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.initialize();
};
SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) {
SnapSerializer.prototype.loadScene = function (xmlNode, remixID) {
// private
var project = {sprites: {}},
var scene = new Scene(),
model,
nameID;
this.project = project;
this.scene = scene;
model = {project: xmlNode };
model = {scene: xmlNode };
if (+xmlNode.attributes.version > this.version) {
throw 'Project uses newer version of Serializer';
}
@ -345,8 +361,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(
@ -356,138 +372,140 @@ 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');
project.globalVariables = new VariableFrame();
model.globalVariables = model.scene.childNamed('variables');
scene.unifiedPalette = model.scene.attributes.unifiedPalette === 'true';
/* Stage */
model.stage = model.project.require('stage');
model.stage = model.scene.require('stage');
StageMorph.prototype.frameRate = 0;
project.stage = new StageMorph(project.globalVariables);
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) {
StageMorph.prototype.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);
StageMorph.prototype.dimensions = new Point(480, 360);
scene.stage.setTempo(model.stage.attributes.tempo);
if (model.stage.attributes.width) {
StageMorph.prototype.dimensions.x =
scene.stage.dimensions.x =
Math.max(+model.stage.attributes.width, 240);
}
if (model.stage.attributes.height) {
StageMorph.prototype.dimensions.y =
scene.stage.dimensions.y =
Math.max(+model.stage.attributes.height, 180);
}
project.stage.setExtent(StageMorph.prototype.dimensions);
SpriteMorph.prototype.useFlatLineEnds =
scene.stage.setExtent(scene.stage.dimensions);
scene.useFlatLineEnds =
model.stage.attributes.lines === 'flat';
BooleanSlotMorph.prototype.isTernary =
model.stage.attributes.ternary !== 'false';
Process.prototype.enableHyperOps =
scene.enableHyperOps =
model.stage.attributes.hyperops !== 'false';
project.stage.isThreadSafe =
scene.stage.isThreadSafe =
model.stage.attributes.threadsafe === 'true';
StageMorph.prototype.enableCodeMapping =
scene.enableCodeMapping =
model.stage.attributes.codify === 'true';
StageMorph.prototype.enableInheritance =
scene.enableInheritance =
model.stage.attributes.inheritance !== 'false';
StageMorph.prototype.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) {
StageMorph.prototype.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 => StageMorph.prototype.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 => StageMorph.prototype.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;
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)
);
// 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.spritesDict[
sprite.inheritanceInfo.exemplar
];
if (exemplar) {
@ -497,14 +515,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.spritesDict[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);
@ -541,7 +559,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) {
if (model.globalVariables) {
this.loadVariables(
project.globalVariables,
scene.globalVariables,
model.globalVariables
);
}
@ -557,7 +575,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode, remixID) {
target = Object.prototype.hasOwnProperty.call(
model.attributes,
'scope'
) ? project.sprites[model.attributes.scope] : null;
) ? scene.spritesDict[model.attributes.scope] : null;
// determine whether the watcher is hidden, slightly
// complicated to retain backward compatibility
@ -575,7 +593,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
@ -595,12 +613,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
@ -621,25 +639,22 @@ 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 = []
);
this.objects = {};
return project;
return scene.initialize();
};
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,36 +668,33 @@ 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;
};
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;
project = this.project = {
globalVariables: ide.globalVariables,
stage: ide.stage,
sprites: {}
};
project.sprites[project.stage.name] = project.stage;
this.scene = new Scene(ide.stage);
scene = this.scene;
scene.spritesDict[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.spritesDict[sprite.name] = sprite;
}
if (model.attributes.color) {
sprite.color = this.loadColor(model.attributes.color);
@ -697,7 +709,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(
@ -713,10 +725,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.spritesDict[
sprite.inheritanceInfo.exemplar
];
if (exemplar) {
@ -724,14 +736,14 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) {
}
}
if (sprite.nestingInfo) { // only sprites may have nesting info
anchor = project.sprites[sprite.nestingInfo.anchor];
anchor = scene.spritesDict[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);
@ -740,7 +752,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) {
});
this.objects = {};
this.project = {};
this.scene = new Scene();
this.mediaDict = {};
ide.stage.fixLayout();
@ -1111,10 +1123,10 @@ 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
if (!this.project.stage) {
this.project.stage = object.parentThatIsA(StageMorph);
this.project.targetStage = this.project.stage;
// whole scene
if (!this.scene.stage) {
this.scene.stage = object.parentThatIsA(StageMorph);
this.scene.targetStage = this.scene.stage;
}
model.children.forEach(child => {
@ -1174,15 +1186,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
);
}
@ -1399,13 +1411,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.spritesDict[model.attributes.name] = v;
}
if (model.attributes.idx) {
v.idx = +model.attributes.idx;
@ -1423,7 +1435,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'
@ -1605,48 +1617,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
@ -1658,24 +1628,39 @@ Array.prototype.toXML = function (serializer) {
);
};
// Sprites
// Scenes & multi-scene projects
StageMorph.prototype.toXML = function (serializer) {
var thumbnail = normalizeCanvas(
this.thumbnail(SnapSerializer.prototype.thumbnailSize),
true
),
thumbdata,
costumeIdx = this.getCostumeIdx(),
ide = this.parentThatIsA(IDE_Morph);
Project.prototype.toXML = function (serializer) {
var thumbdata;
// catch cross-origin tainting exception when using SVG costumes
// thumb data catch cross-origin tainting exception when using SVG costumes
try {
thumbdata = thumbnail.toDataURL('image/png');
thumbdata = this.thumbnail.toDataURL('image/png');
} catch (error) {
thumbdata = null;
}
return serializer.format(
'<project name="@" app="@" version="@">' +
'<notes>$</notes>' +
'<thumbnail>$</thumbnail>' +
'<scenes select="@">%</scenes>' +
'</project>',
this.name || localize('Untitled'),
serializer.app,
serializer.version,
this.notes || '',
thumbdata,
this.scenes.asArray().indexOf(
this.currentScene) + 1,
serializer.store(this.scenes.itemsArray())
);
};
Scene.prototype.toXML = function (serializer) {
var tmp = new Scene(),
xml;
function code(key) {
var str = '';
Object.keys(StageMorph.prototype[key]).forEach(
@ -1692,12 +1677,43 @@ StageMorph.prototype.toXML = function (serializer) {
return str;
}
tmp.captureGlobalSettings();
this.applyGlobalSettings();
xml = serializer.format(
'<scene name="@" unifiedPalette="@">' +
'<notes>$</notes>' +
'<hidden>$</hidden>' +
'<headers>%</headers>' +
'<code>%</code>' +
'<blocks>%</blocks>' +
'<variables>%</variables>' +
'%' + // stage
'</scene>',
this.name || localize('Untitled'),
this.unifiedPalette,
this.notes || '',
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 costumeIdx = this.getCostumeIdx();
this.removeAllClones();
return serializer.format(
'<project name="@" app="@" version="@">' +
'<notes>$</notes>' +
'<thumbnail>$</thumbnail>' +
'<stage name="@" width="@" height="@" ' +
'<stage width="@" height="@" ' +
'costume="@" color="@,@,@,@" tempo="@" threadsafe="@" ' +
'penlog="@" ' +
'%' +
@ -1716,22 +1732,11 @@ StageMorph.prototype.toXML = function (serializer) {
'<sounds>%</sounds>' +
'<variables>%</variables>' +
'<blocks>%</blocks>' +
'<scripts>%</scripts><sprites>%</sprites>' +
'</stage>' +
'<hidden>$</hidden>' +
'<headers>%</headers>' +
'<code>%</code>' +
'<blocks>%</blocks>' +
'<variables>%</variables>' +
'</project>',
(ide && ide.projectName) ? ide.projectName : localize('Untitled'),
serializer.app,
serializer.version,
(ide && ide.projectNotes) ? ide.projectNotes : '',
thumbdata,
this.name,
StageMorph.prototype.dimensions.x,
StageMorph.prototype.dimensions.y,
'<scripts>%</scripts>' +
'<sprites select="@">%</sprites>' +
'</stage>',
this.dimensions.x,
this.dimensions.y,
costumeIdx,
this.color.r,
this.color.g,
@ -1763,23 +1768,14 @@ StageMorph.prototype.toXML = function (serializer) {
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) : ''
serializer.root.sprites.asArray().indexOf(
serializer.root.currentSprite) + 1,
serializer.store(this.children)
);
};
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'),

Wyświetl plik

@ -4657,6 +4657,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) {