summaryrefslogtreecommitdiff
path: root/objects.js
diff options
context:
space:
mode:
Diffstat (limited to 'objects.js')
-rw-r--r--objects.js198
1 files changed, 162 insertions, 36 deletions
diff --git a/objects.js b/objects.js
index 69376c8..3f95385 100644
--- a/objects.js
+++ b/objects.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -105,7 +105,7 @@ InspectorMorph, ListMorph, Math, MenuItemMorph, MenuMorph, Morph,
MorphicPreferences, MouseSensorMorph, Node, Object, PenMorph, Point,
Rectangle, ScrollFrameMorph, ShadowMorph, SliderButtonMorph,
SliderMorph, String, StringFieldMorph, StringMorph, TextMorph,
-TriggerMorph, WorldMorph, clone, contains, copy, degrees, detect,
+TriggerMorph, WorldMorph, contains, copy, degrees, detect,
document, getDocumentPositionOf, isNaN, isObject, isString, newCanvas,
nop, parseFloat, radians, standardSettings, touchScreenSettings,
useBlurredShadows, version, window, modules, IDE_Morph, VariableDialogMorph,
@@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.objects = '2014-November-24';
+modules.objects = '2015-June-25';
var SpriteMorph;
var StageMorph;
@@ -581,11 +581,22 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'control',
spec: 'when %keyHat key pressed'
},
+
+ /* migrated to a newer block version:
+
receiveClick: {
type: 'hat',
category: 'control',
spec: 'when I am clicked'
},
+ */
+
+ receiveInteraction: {
+ type: 'hat',
+ category: 'control',
+ spec: 'when I am %interaction',
+ defaults: ['clicked']
+ },
receiveMessage: {
type: 'hat',
category: 'control',
@@ -793,6 +804,12 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sensing',
spec: 'frames'
},
+ reportThreadCount: {
+ dev: true,
+ type: 'reporter',
+ category: 'sensing',
+ spec: 'processes'
+ },
doAsk: {
type: 'command',
category: 'sensing',
@@ -1151,6 +1168,13 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'lists',
spec: 'map %repRing over %l'
},
+ doForEach: {
+ dev: true,
+ type: 'command',
+ category: 'lists',
+ spec: 'for %upvar in %l %cs',
+ defaults: [localize('each item')]
+ },
// Code mapping - experimental
doMapCodeOrHeader: { // experimental
@@ -1192,6 +1216,10 @@ SpriteMorph.prototype.initBlockMigrations = function () {
doStopBlock: {
selector: 'doStopThis',
inputs: [['this block']]
+ },
+ receiveClick: {
+ selector: 'receiveInteraction',
+ inputs: [['clicked']]
}
};
};
@@ -1240,8 +1268,6 @@ SpriteMorph.prototype.blockAlternatives = {
setSize: ['changeSize'],
// control:
- receiveGo: ['receiveClick'],
- receiveClick: ['receiveGo'],
doBroadcast: ['doBroadcastAndWait'],
doBroadcastAndWait: ['doBroadcast'],
doIf: ['doIfElse', 'doUntil'],
@@ -1302,6 +1328,7 @@ SpriteMorph.prototype.init = function (globals) {
this.anchor = null;
this.nestingScale = 1;
this.rotatesWithAnchor = true;
+ this.layers = null; // cache for dragging nested sprites, don't serialize
this.blocksCache = {}; // not to be serialized (!)
this.paletteCache = {}; // not to be serialized (!)
@@ -1406,15 +1433,13 @@ SpriteMorph.prototype.setName = function (string) {
SpriteMorph.prototype.drawNew = function () {
var myself = this,
- currentCenter = this.center(),
+ currentCenter,
facing, // actual costume heading based on my rotation style
isFlipped,
- isLoadingCostume = this.costume &&
- typeof this.costume.loaded === 'function',
+ isLoadingCostume,
cst,
pic, // (flipped copy of) actual costume based on my rotation style
- stageScale = this.parent instanceof StageMorph ?
- this.parent.scale : 1,
+ stageScale,
newX,
corners = [],
origin,
@@ -1428,6 +1453,11 @@ SpriteMorph.prototype.drawNew = function () {
this.wantsRedraw = true;
return;
}
+ currentCenter = this.center();
+ isLoadingCostume = this.costume &&
+ typeof this.costume.loaded === 'function';
+ stageScale = this.parent instanceof StageMorph ?
+ this.parent.scale : 1;
facing = this.rotationStyle ? this.heading : 90;
if (this.rotationStyle === 2) {
facing = 90;
@@ -1575,7 +1605,7 @@ SpriteMorph.prototype.blockForSelector = function (selector, setDefaults) {
: new ReporterBlockMorph(info.type === 'predicate');
block.color = this.blockColor[info.category];
block.category = info.category;
- block.selector = selector;
+ block.selector = migration ? migration.selector : selector;
if (contains(['reifyReporter', 'reifyPredicate'], block.selector)) {
block.isStatic = true;
}
@@ -1815,7 +1845,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('receiveGo'));
blocks.push(block('receiveKey'));
- blocks.push(block('receiveClick'));
+ blocks.push(block('receiveInteraction'));
blocks.push(block('receiveMessage'));
blocks.push('-');
blocks.push(block('doBroadcast'));
@@ -1911,6 +1941,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
txt.setColor(this.paletteTextColor);
blocks.push(txt);
blocks.push('-');
+ blocks.push(watcherToggle('reportThreadCount'));
+ blocks.push(block('reportThreadCount'));
blocks.push(block('colorFiltered'));
blocks.push(block('reportStackSize'));
blocks.push(block('reportFrameCount'));
@@ -2063,6 +2095,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(txt);
blocks.push('-');
blocks.push(block('reportMap'));
+ blocks.push('-');
+ blocks.push(block('doForEach'));
}
/////////////////////////////////
@@ -2625,7 +2659,9 @@ SpriteMorph.prototype.userMenu = function () {
menu.addItem("duplicate", 'duplicate');
menu.addItem("delete", 'remove');
menu.addItem("move", 'move');
- menu.addItem("edit", 'edit');
+ if (!this.isClone) {
+ menu.addItem("edit", 'edit');
+ }
menu.addLine();
if (this.anchor) {
menu.addItem(
@@ -2641,6 +2677,7 @@ SpriteMorph.prototype.userMenu = function () {
};
SpriteMorph.prototype.exportSprite = function () {
+ if (this.isClone) {return; }
var ide = this.parentThatIsA(IDE_Morph);
if (ide) {
ide.exportSprite(this);
@@ -2681,7 +2718,7 @@ SpriteMorph.prototype.remove = function () {
SpriteMorph.prototype.createClone = function () {
var stage = this.parentThatIsA(StageMorph);
- if (stage && stage.cloneCount <= 300) {
+ if (stage && stage.cloneCount <= 1000) {
this.fullCopy().clonify(stage);
}
};
@@ -2737,7 +2774,7 @@ SpriteMorph.prototype.setColor = function (aColor) {
var x = this.xPosition(),
y = this.yPosition();
if (!this.color.eq(aColor)) {
- this.color = aColor;
+ this.color = aColor.copy();
this.drawNew();
this.gotoXY(x, y);
}
@@ -2971,7 +3008,7 @@ SpriteMorph.prototype.applyGraphicsEffects = function (canvas) {
var i;
if (value !== 0) {
for (i = 0; i < p.length; i += 4) {
- p[i] += value; //255 = 100% of this color
+ p[i] += value; //255 = 100% of this color
p[i + 1] += value;
p[i + 2] += value;
}
@@ -3044,7 +3081,7 @@ SpriteMorph.prototype.setEffect = function (effect, value) {
if (eff === 'ghost') {
this.alpha = 1 - Math.min(Math.max(+value || 0, 0), 100) / 100;
} else {
- this.graphicsValues[eff] = value;
+ this.graphicsValues[eff] = +value;
}
this.drawNew();
this.changed();
@@ -3059,7 +3096,7 @@ SpriteMorph.prototype.changeEffect = function (effect, value) {
if (eff === 'ghost') {
this.setEffect(effect, this.getGhostEffect() + (+value || 0));
} else {
- this.setEffect(effect, this.graphicsValues[eff] + value);
+ this.setEffect(effect, +this.graphicsValues[eff] + (+value));
}
};
@@ -3134,13 +3171,11 @@ SpriteMorph.prototype.positionTalkBubble = function () {
bubble.changed();
};
-// dragging and dropping adjustments b/c of talk bubbles
+// dragging and dropping adjustments b/c of talk bubbles and parts
SpriteMorph.prototype.prepareToBeGrabbed = function (hand) {
- var bubble = this.talkBubble();
- if (!bubble) {return null; }
this.removeShadow();
- bubble.hide();
+ this.recordLayers();
if (!this.bounds.containsPoint(hand.position())) {
this.setCenter(hand.position());
}
@@ -3148,7 +3183,9 @@ SpriteMorph.prototype.prepareToBeGrabbed = function (hand) {
};
SpriteMorph.prototype.justDropped = function () {
+ this.restoreLayers();
this.positionTalkBubble();
+ this.receiveUserInteraction('dropped');
};
// SpriteMorph drawing:
@@ -3238,8 +3275,11 @@ SpriteMorph.prototype.setCenter = function (aPoint, justMe) {
SpriteMorph.prototype.nestingBounds = function () {
// same as fullBounds(), except that it uses "parts" instead of children
- var result;
- result = this.bounds;
+ // and special cases the costume-less "arrow" shape's bounding box
+ var result = this.bounds;
+ if (!this.costume && this.penBounds) {
+ result = this.penBounds.translateBy(this.position());
+ }
this.parts.forEach(function (part) {
if (part.isVisible) {
result = result.merge(part.nestingBounds());
@@ -3250,7 +3290,7 @@ SpriteMorph.prototype.nestingBounds = function () {
// SpriteMorph motion primitives
-Morph.prototype.setPosition = function (aPoint, justMe) {
+SpriteMorph.prototype.setPosition = function (aPoint, justMe) {
// override the inherited default to make sure my parts follow
// unless it's justMe
var delta = aPoint.subtract(this.topLeft());
@@ -3469,7 +3509,10 @@ SpriteMorph.prototype.allHatBlocksFor = function (message) {
if (morph.selector) {
if (morph.selector === 'receiveMessage') {
event = morph.inputs()[0].evaluate();
- return event === message || (event instanceof Array);
+ return event === message
+ || (event instanceof Array
+ && message !== '__shout__go__'
+ && message !== '__clone__init__');
}
if (morph.selector === 'receiveGo') {
return message === '__shout__go__';
@@ -3477,9 +3520,6 @@ SpriteMorph.prototype.allHatBlocksFor = function (message) {
if (morph.selector === 'receiveOnClone') {
return message === '__clone__init__';
}
- if (morph.selector === 'receiveClick') {
- return message === '__click__';
- }
}
return false;
});
@@ -3496,13 +3536,37 @@ SpriteMorph.prototype.allHatBlocksForKey = function (key) {
});
};
+SpriteMorph.prototype.allHatBlocksForInteraction = function (interaction) {
+ return this.scripts.children.filter(function (morph) {
+ if (morph.selector) {
+ if (morph.selector === 'receiveInteraction') {
+ return morph.inputs()[0].evaluate()[0] === interaction;
+ }
+ }
+ return false;
+ });
+};
+
// SpriteMorph events
SpriteMorph.prototype.mouseClickLeft = function () {
- var stage = this.parentThatIsA(StageMorph),
- hats = this.allHatBlocksFor('__click__'),
- procs = [];
+ return this.receiveUserInteraction('clicked');
+};
+
+SpriteMorph.prototype.mouseEnter = function () {
+ return this.receiveUserInteraction('mouse-entered');
+};
+
+SpriteMorph.prototype.mouseDownLeft = function () {
+ return this.receiveUserInteraction('pressed');
+};
+SpriteMorph.prototype.receiveUserInteraction = function (interaction) {
+ var stage = this.parentThatIsA(StageMorph),
+ procs = [],
+ hats;
+ if (!stage) {return; } // currently dragged
+ hats = this.allHatBlocksForInteraction(interaction);
hats.forEach(function (block) {
procs.push(stage.threads.startProcess(block, stage.isThreadSafe));
});
@@ -3510,6 +3574,7 @@ SpriteMorph.prototype.mouseClickLeft = function () {
};
SpriteMorph.prototype.mouseDoubleClick = function () {
+ if (this.isClone) {return; }
this.edit();
};
@@ -3567,6 +3632,16 @@ SpriteMorph.prototype.reportMouseY = function () {
return 0;
};
+// SpriteMorph thread count (for debugging)
+
+SpriteMorph.prototype.reportThreadCount = function () {
+ var stage = this.parentThatIsA(StageMorph);
+ if (stage) {
+ return stage.threads.processes.length;
+ }
+ return 0;
+};
+
// SpriteMorph variable watchers (for palette checkbox toggling)
SpriteMorph.prototype.findVariableWatcher = function (varName) {
@@ -4011,6 +4086,33 @@ SpriteMorph.prototype.allAnchors = function () {
return result;
};
+SpriteMorph.prototype.recordLayers = function () {
+ var stage = this.parentThatIsA(StageMorph);
+ if (!stage) {
+ this.layerCache = null;
+ return;
+ }
+ this.layers = this.allParts();
+ this.layers.forEach(function (part) {
+ var bubble = part.talkBubble();
+ if (bubble) {bubble.hide(); }
+ });
+ this.layers.sort(function (x, y) {
+ return stage.children.indexOf(x) < stage.children.indexOf(y) ?
+ -1 : 1;
+ });
+};
+
+SpriteMorph.prototype.restoreLayers = function () {
+ if (this.layers && this.layers.length > 1) {
+ this.layers.forEach(function (sprite) {
+ sprite.comeToFront();
+ sprite.positionTalkBubble();
+ });
+ }
+ this.layers = null;
+};
+
// SpriteMorph highlighting
SpriteMorph.prototype.addHighlight = function (oldHighlight) {
@@ -4129,6 +4231,7 @@ SpriteMorph.prototype.mouseEnterDragging = function () {
};
SpriteMorph.prototype.mouseLeave = function () {
+ this.receiveUserInteraction('mouse-departed');
if (!this.enableNesting) {return; }
this.removeHighlight();
};
@@ -4959,7 +5062,7 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('receiveGo'));
blocks.push(block('receiveKey'));
- blocks.push(block('receiveClick'));
+ blocks.push(block('receiveInteraction'));
blocks.push(block('receiveMessage'));
blocks.push('-');
blocks.push(block('doBroadcast'));
@@ -5047,6 +5150,8 @@ StageMorph.prototype.blockTemplates = function (category) {
txt.setColor(this.paletteTextColor);
blocks.push(txt);
blocks.push('-');
+ blocks.push(watcherToggle('reportThreadCount'));
+ blocks.push(block('reportThreadCount'));
blocks.push(block('colorFiltered'));
blocks.push(block('reportStackSize'));
blocks.push(block('reportFrameCount'));
@@ -5195,6 +5300,8 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(txt);
blocks.push('-');
blocks.push(block('reportMap'));
+ blocks.push('-');
+ blocks.push(block('doForEach'));
}
/////////////////////////////////
@@ -5333,7 +5440,7 @@ StageMorph.prototype.thumbnail = function (extentPoint, excludedSprite) {
this.dimensions.y * this.scale
);
this.children.forEach(function (morph) {
- if (morph !== excludedSprite) {
+ if (morph.isVisible && (morph !== excludedSprite)) {
fb = morph.fullBounds();
fimg = morph.fullImage();
if (fimg.width && fimg.height) {
@@ -5495,6 +5602,9 @@ StageMorph.prototype.watcherFor =
StageMorph.prototype.getLastAnswer
= SpriteMorph.prototype.getLastAnswer;
+StageMorph.prototype.reportThreadCount
+ = SpriteMorph.prototype.reportThreadCount;
+
// StageMorph message broadcasting
StageMorph.prototype.allMessageNames
@@ -5506,11 +5616,27 @@ StageMorph.prototype.allHatBlocksFor
StageMorph.prototype.allHatBlocksForKey
= SpriteMorph.prototype.allHatBlocksForKey;
+StageMorph.prototype.allHatBlocksForInteraction
+ = SpriteMorph.prototype.allHatBlocksForInteraction;
+
// StageMorph events
StageMorph.prototype.mouseClickLeft
= SpriteMorph.prototype.mouseClickLeft;
+StageMorph.prototype.mouseEnter
+ = SpriteMorph.prototype.mouseEnter;
+
+StageMorph.prototype.mouseLeave = function () {
+ this.receiveUserInteraction('mouse-departed');
+};
+
+StageMorph.prototype.mouseDownLeft
+ = SpriteMorph.prototype.mouseDownLeft;
+
+StageMorph.prototype.receiveUserInteraction
+ = SpriteMorph.prototype.receiveUserInteraction;
+
// StageMorph custom blocks
StageMorph.prototype.deleteAllBlockInstances
@@ -6306,7 +6432,7 @@ Note.prototype.play = function () {
if (!this.oscillator.stop) {
this.oscillator.stop = this.oscillator.noteOff;
}
- this.oscillator.type = 0;
+ this.oscillator.type = 'sine';
this.oscillator.frequency.value =
Math.pow(2, (this.pitch - 69) / 12) * 440;
this.oscillator.connect(this.gainNode);
@@ -6743,7 +6869,7 @@ WatcherMorph.prototype.object = function () {
WatcherMorph.prototype.isGlobal = function (selector) {
return contains(
['getLastAnswer', 'getLastMessage', 'getTempo', 'getTimer',
- 'reportMouseX', 'reportMouseY'],
+ 'reportMouseX', 'reportMouseY', 'reportThreadCount'],
selector
);
};