summaryrefslogtreecommitdiff
path: root/objects.js
diff options
context:
space:
mode:
Diffstat (limited to 'objects.js')
-rw-r--r--objects.js237
1 files changed, 186 insertions, 51 deletions
diff --git a/objects.js b/objects.js
index 4be213a..db5ab88 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!.
@@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.objects = '2014-October-08';
+modules.objects = '2015-April-15';
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;
}
@@ -1674,6 +1704,20 @@ SpriteMorph.prototype.blockTemplates = function (category) {
return menu;
}
+ function addVar(pair) {
+ if (pair) {
+ if (myself.variables.silentFind(pair[0])) {
+ myself.inform('that name is already in use');
+ } else {
+ myself.addVariable(pair[0], pair[1]);
+ myself.toggleVariableWatcher(pair[0], pair[1]);
+ myself.blocksCache[cat] = null;
+ myself.paletteCache[cat] = null;
+ myself.parentThatIsA(IDE_Morph).refreshPalette();
+ }
+ }
+ }
+
if (cat === 'motion') {
blocks.push(block('forward'));
@@ -1801,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'));
@@ -1897,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'));
@@ -1967,15 +2013,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
function () {
new VariableDialogMorph(
null,
- function (pair) {
- if (pair && !myself.variables.silentFind(pair[0])) {
- myself.addVariable(pair[0], pair[1]);
- myself.toggleVariableWatcher(pair[0], pair[1]);
- myself.blocksCache[cat] = null;
- myself.paletteCache[cat] = null;
- myself.parentThatIsA(IDE_Morph).refreshPalette();
- }
- },
+ addVar,
myself
).prompt(
'Variable name',
@@ -2057,6 +2095,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(txt);
blocks.push('-');
blocks.push(block('reportMap'));
+ blocks.push('-');
+ blocks.push(block('doForEach'));
}
/////////////////////////////////
@@ -2164,8 +2204,8 @@ SpriteMorph.prototype.freshPalette = function (category) {
var defs = SpriteMorph.prototype.blocks,
hiddens = StageMorph.prototype.hiddenPrimitives;
return Object.keys(hiddens).some(function (any) {
- return defs[any].category === category ||
- contains((more[category] || []), any);
+ return !isNil(defs[any]) && (defs[any].category === category
+ || contains((more[category] || []), any));
});
}
@@ -2204,7 +2244,7 @@ SpriteMorph.prototype.freshPalette = function (category) {
var hiddens = StageMorph.prototype.hiddenPrimitives,
defs = SpriteMorph.prototype.blocks;
Object.keys(hiddens).forEach(function (sel) {
- if (defs[sel].category === category) {
+ if (defs[sel] && (defs[sel].category === category)) {
delete StageMorph.prototype.hiddenPrimitives[sel];
}
});
@@ -2619,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(
@@ -2635,6 +2677,7 @@ SpriteMorph.prototype.userMenu = function () {
};
SpriteMorph.prototype.exportSprite = function () {
+ if (this.isCoone) {return; }
var ide = this.parentThatIsA(IDE_Morph);
if (ide) {
ide.exportSprite(this);
@@ -2731,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);
}
@@ -3038,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();
@@ -3053,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));
}
};
@@ -3128,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());
}
@@ -3142,7 +3183,9 @@ SpriteMorph.prototype.prepareToBeGrabbed = function (hand) {
};
SpriteMorph.prototype.justDropped = function () {
+ this.restoreLayers();
this.positionTalkBubble();
+ this.receiveUserInteraction('dropped');
};
// SpriteMorph drawing:
@@ -3232,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());
@@ -3471,9 +3517,6 @@ SpriteMorph.prototype.allHatBlocksFor = function (message) {
if (morph.selector === 'receiveOnClone') {
return message === '__clone__init__';
}
- if (morph.selector === 'receiveClick') {
- return message === '__click__';
- }
}
return false;
});
@@ -3490,13 +3533,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));
});
@@ -3504,6 +3571,7 @@ SpriteMorph.prototype.mouseClickLeft = function () {
};
SpriteMorph.prototype.mouseDoubleClick = function () {
+ if (this.isClone) {return; }
this.edit();
};
@@ -3561,6 +3629,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) {
@@ -4005,6 +4083,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) {
@@ -4123,6 +4228,7 @@ SpriteMorph.prototype.mouseEnterDragging = function () {
};
SpriteMorph.prototype.mouseLeave = function () {
+ this.receiveUserInteraction('mouse-departed');
if (!this.enableNesting) {return; }
this.removeHighlight();
};
@@ -4858,6 +4964,20 @@ StageMorph.prototype.blockTemplates = function (category) {
);
}
+ function addVar(pair) {
+ if (pair) {
+ if (myself.variables.silentFind(pair[0])) {
+ myself.inform('that name is already in use');
+ } else {
+ myself.addVariable(pair[0], pair[1]);
+ myself.toggleVariableWatcher(pair[0], pair[1]);
+ myself.blocksCache[cat] = null;
+ myself.paletteCache[cat] = null;
+ myself.parentThatIsA(IDE_Morph).refreshPalette();
+ }
+ }
+ }
+
if (cat === 'motion') {
txt = new TextMorph(localize(
@@ -4939,7 +5059,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'));
@@ -5027,6 +5147,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'));
@@ -5099,15 +5221,7 @@ StageMorph.prototype.blockTemplates = function (category) {
function () {
new VariableDialogMorph(
null,
- function (pair) {
- if (pair && !myself.variables.silentFind(pair[0])) {
- myself.addVariable(pair[0], pair[1]);
- myself.toggleVariableWatcher(pair[0], pair[1]);
- myself.blocksCache[cat] = null;
- myself.paletteCache[cat] = null;
- myself.parentThatIsA(IDE_Morph).refreshPalette();
- }
- },
+ addVar,
myself
).prompt(
'Variable name',
@@ -5183,6 +5297,8 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(txt);
blocks.push('-');
blocks.push(block('reportMap'));
+ blocks.push('-');
+ blocks.push(block('doForEach'));
}
/////////////////////////////////
@@ -5321,7 +5437,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) {
@@ -5483,6 +5599,9 @@ StageMorph.prototype.watcherFor =
StageMorph.prototype.getLastAnswer
= SpriteMorph.prototype.getLastAnswer;
+StageMorph.prototype.reportThreadCount
+ = SpriteMorph.prototype.reportThreadCount;
+
// StageMorph message broadcasting
StageMorph.prototype.allMessageNames
@@ -5494,11 +5613,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
@@ -6731,7 +6866,7 @@ WatcherMorph.prototype.object = function () {
WatcherMorph.prototype.isGlobal = function (selector) {
return contains(
['getLastAnswer', 'getLastMessage', 'getTempo', 'getTimer',
- 'reportMouseX', 'reportMouseY'],
+ 'reportMouseX', 'reportMouseY', 'reportThreadCount'],
selector
);
};