diff options
Diffstat (limited to 'objects.js')
| -rw-r--r-- | objects.js | 237 |
1 files changed, 186 insertions, 51 deletions
@@ -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 ); }; |
