Prepare for v4.0.3

* fixed occasional horizontal rendering artifacts in block “holes”
(C-shaped slots and Ring holes)
* improved user editing of input slots and fixed cursor behavior (note:
the text cursor inside input slots also blinks again, as it should have)
* always export comments attached to prototype hat blocks in the block
editor along with script pic
* when first opening a block editor on a custom block definition make
it big enough to show everything (as much as fits on the screen)
* remember block editor position and dimensions for each edited custom
block definition when acknowledging (pressing OK or APPLY) for the
session
* speed up stacking of commands and loading of projects by suppressing
redundant block redraws
* introducing a “grab threshold” preference to suppress accidental
grabbing through micro-movements of the hand. This addresses the
“cannot-click-on-drop-downs-in-Chrome-under-Windows” set of bug
reports, and also the issues that arise out of accidentally dragging
off a copy of a parameter blob when trying to just click on it to
rename it
* new hidden (shift-click) option to adjust the grab threshold in the
settings menu for the current session
* expand list watchers inside result bubbles and speech/thought
balloons to show everything (as much of the list’s first level as fist
into either the scripting area for result bubbles or the stage for
speech balloons - note resizing the stage affects the size of the list
watchers inside speech balloons, i.e. making the stage bigger increases
the number of visible elements even while the balloon is showing)
* fixed a bug that make an occasional gray rectangle appear at the
mouse pointer
* added a way to invoke blocks synchronously in JavaScript - under
construction (possibly to be used for generic “When” hat blocks and
user-customizable drop-downs)
* added methods to cache morphs’ fullImage and fullBounds while dragging
* Reporters (also nested reporters) and sprite icons (in the corral)
are now semi-transparent when being dragged, so you can see possible
drop target halos /through/ them
* in “prefer empty slot drops” mode (default) it is now much harder to
drop reporters onto non-static C-slots inside custom blocks (e.g. in
FOR loops) and onto variadic input arrowheads (to replace the whole
input with an input list)
* ScriptsMorphs are now noticing transparent clicks (addresses #997)
* %interaction slots are now static, fixed #982 (it is no longer
possible to drop reporters into the input slot of a “When I am…” hat
block (never was intended that it should be possible)
* fixed ctrl-f for the block editor in all situations (thanks, Brian,
for the bug report)
dev
Jens Mönig 2015-11-16 12:18:40 +01:00
rodzic 72bf4cfb8c
commit 6061403a89
9 zmienionych plików z 400 dodań i 167 usunięć

183
blocks.js
Wyświetl plik

@ -156,7 +156,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2015-October-02'; modules.blocks = '2015-November-16';
var SyntaxElementMorph; var SyntaxElementMorph;
var BlockMorph; var BlockMorph;
@ -349,22 +349,18 @@ function SyntaxElementMorph() {
this.init(); this.init();
} }
SyntaxElementMorph.prototype.init = function () { SyntaxElementMorph.prototype.init = function (silently) {
this.cachedClr = null; this.cachedClr = null;
this.cachedClrBright = null; this.cachedClrBright = null;
this.cachedClrDark = null; this.cachedClrDark = null;
this.isStatic = false; // if true, I cannot be exchanged this.isStatic = false; // if true, I cannot be exchanged
SyntaxElementMorph.uber.init.call(this); SyntaxElementMorph.uber.init.call(this, silently);
this.defaults = []; this.defaults = [];
this.cachedInputs = null; this.cachedInputs = null;
}; };
// SyntaxElementMorph stepping:
SyntaxElementMorph.prototype.step = null;
// SyntaxElementMorph accessing: // SyntaxElementMorph accessing:
SyntaxElementMorph.prototype.parts = function () { SyntaxElementMorph.prototype.parts = function () {
@ -472,8 +468,7 @@ SyntaxElementMorph.prototype.replaceInput = function (oldArg, newArg) {
var scripts = this.parentThatIsA(ScriptsMorph), var scripts = this.parentThatIsA(ScriptsMorph),
replacement = newArg, replacement = newArg,
idx = this.children.indexOf(oldArg), idx = this.children.indexOf(oldArg),
i = 0, i = 0;
nb;
// try to find the ArgLabel embedding the newArg, // try to find the ArgLabel embedding the newArg,
// used for the undrop() feature // used for the undrop() feature
@ -511,13 +506,6 @@ SyntaxElementMorph.prototype.replaceInput = function (oldArg, newArg) {
oldArg.moveBy(replacement.extent()); oldArg.moveBy(replacement.extent());
oldArg.fixBlockColor(); oldArg.fixBlockColor();
} }
} else if (oldArg instanceof CommandSlotMorph) {
nb = oldArg.nestedBlock();
if (nb) {
scripts.add(nb);
nb.moveBy(replacement.extent());
nb.fixBlockColor();
}
} }
if (replacement instanceof MultiArgMorph if (replacement instanceof MultiArgMorph
|| replacement instanceof ArgLabelMorph || replacement instanceof ArgLabelMorph
@ -703,10 +691,11 @@ SyntaxElementMorph.prototype.dark = function () {
// SyntaxElementMorph color changing: // SyntaxElementMorph color changing:
SyntaxElementMorph.prototype.setColor = function (aColor) { SyntaxElementMorph.prototype.setColor = function (aColor, silently) {
if (aColor) { if (aColor) {
if (!this.color.eq(aColor)) { if (!this.color.eq(aColor)) {
this.color = aColor; this.color = aColor;
if (silently) {return; }
this.drawNew(); this.drawNew();
this.children.forEach(function (child) { this.children.forEach(function (child) {
child.drawNew(); child.drawNew();
@ -942,6 +931,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
}, },
true // read-only true // read-only
); );
part.isStatic = true;
break; break;
case '%dates': case '%dates':
part = new InputSlotMorph( part = new InputSlotMorph(
@ -1302,6 +1292,11 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
case '%cs': case '%cs':
part = new CSlotMorph(); // non-static part = new CSlotMorph(); // non-static
break; break;
case '%cl':
part = new CSlotMorph();
part.isStatic = true; // rejects reporter drops
part.isLambda = true; // auto-reifies nested script
break;
case '%clr': case '%clr':
part = new ColorSlotMorph(); part = new ColorSlotMorph();
part.isStatic = true; part.isStatic = true;
@ -1501,7 +1496,7 @@ SyntaxElementMorph.prototype.isObjInputFragment = function () {
// SyntaxElementMorph layout: // SyntaxElementMorph layout:
SyntaxElementMorph.prototype.fixLayout = function () { SyntaxElementMorph.prototype.fixLayout = function (silently) {
var nb, var nb,
parts = this.parts(), parts = this.parts(),
myself = this, myself = this,
@ -1682,8 +1677,8 @@ SyntaxElementMorph.prototype.fixLayout = function () {
} }
} }
// set my extent: // set my extent (silently, because we'll redraw later anyway):
this.setExtent(new Point(blockWidth, blockHeight)); this.silentSetExtent(new Point(blockWidth, blockHeight));
// adjust CSlots // adjust CSlots
parts.forEach(function (part) { parts.forEach(function (part) {
@ -1697,7 +1692,7 @@ SyntaxElementMorph.prototype.fixLayout = function () {
}); });
// redraw in order to erase CSlot backgrounds // redraw in order to erase CSlot backgrounds
this.drawNew(); if (!silently) {this.drawNew(); }
// position next block: // position next block:
if (nb) { if (nb) {
@ -1777,6 +1772,7 @@ SyntaxElementMorph.prototype.showBubble = function (value, exportPic) {
morphToShow.update(true); morphToShow.update(true);
morphToShow.step = value.update; morphToShow.step = value.update;
morphToShow.isDraggable = false; morphToShow.isDraggable = false;
morphToShow.expand(this.parentThatIsA(ScrollFrameMorph).extent());
isClickable = true; isClickable = true;
} else if (value instanceof Morph) { } else if (value instanceof Morph) {
img = value.fullImage(); img = value.fullImage();
@ -1954,7 +1950,9 @@ SyntaxElementMorph.prototype.endLayout = function () {
%lst - chameleon colored rectangular drop-down for list names %lst - chameleon colored rectangular drop-down for list names
%b - chameleon colored hexagonal slot (for predicates) %b - chameleon colored hexagonal slot (for predicates)
%l - list icon %l - list icon
%c - C-shaped command slot %c - C-shaped command slot, special form for primitives
%cs - C-shaped, auto-reifying, accepts reporter drops
%cl - C-shaped, auto-reifying, rejects reporters
%clr - interactive color slot %clr - interactive color slot
%t - inline variable reporter template %t - inline variable reporter template
%anyUE - white rectangular type-in slot, unevaluated if replaced %anyUE - white rectangular type-in slot, unevaluated if replaced
@ -2027,7 +2025,7 @@ function BlockMorph() {
this.init(); this.init();
} }
BlockMorph.prototype.init = function () { BlockMorph.prototype.init = function (silently) {
this.selector = null; // name of method to be triggered this.selector = null; // name of method to be triggered
this.blockSpec = ''; // formal description of label and arguments this.blockSpec = ''; // formal description of label and arguments
this.comment = null; // optional "sticky" comment morph this.comment = null; // optional "sticky" comment morph
@ -2036,7 +2034,7 @@ BlockMorph.prototype.init = function () {
this.instantiationSpec = null; // spec to set upon fullCopy() of template this.instantiationSpec = null; // spec to set upon fullCopy() of template
this.category = null; // for zebra coloring (non persistent) this.category = null; // for zebra coloring (non persistent)
BlockMorph.uber.init.call(this); BlockMorph.uber.init.call(this, silently);
this.color = new Color(0, 17, 173); this.color = new Color(0, 17, 173);
this.cashedInputs = null; this.cashedInputs = null;
}; };
@ -2101,7 +2099,7 @@ BlockMorph.prototype.parseSpec = function (spec) {
return result; return result;
}; };
BlockMorph.prototype.setSpec = function (spec) { BlockMorph.prototype.setSpec = function (spec, silently) {
var myself = this, var myself = this,
part, part,
inputIdx = -1; inputIdx = -1;
@ -2144,10 +2142,17 @@ BlockMorph.prototype.setSpec = function (spec) {
} }
}); });
this.blockSpec = spec; this.blockSpec = spec;
this.fixLayout(); this.fixLayout(silently);
this.cachedInputs = null; this.cachedInputs = null;
}; };
BlockMorph.prototype.userSetSpec = function (spec) {
var tb = this.topBlock();
tb.fullChanged();
this.setSpec(spec);
tb.fullChanged();
};
BlockMorph.prototype.buildSpec = function () { BlockMorph.prototype.buildSpec = function () {
// create my blockSpec from my parts - for demo purposes only // create my blockSpec from my parts - for demo purposes only
var myself = this; var myself = this;
@ -2448,6 +2453,7 @@ BlockMorph.prototype.ringify = function () {
center = top.fullBounds().center(); center = top.fullBounds().center();
if (this.parent === null) {return null; } if (this.parent === null) {return null; }
top.fullChanged();
if (this.parent instanceof SyntaxElementMorph) { if (this.parent instanceof SyntaxElementMorph) {
if (this instanceof ReporterBlockMorph) { if (this instanceof ReporterBlockMorph) {
this.parent.silentReplaceInput(this, ring); this.parent.silentReplaceInput(this, ring);
@ -2463,11 +2469,14 @@ BlockMorph.prototype.ringify = function () {
ring.setCenter(center); ring.setCenter(center);
} }
this.fixBlockColor(null, true); this.fixBlockColor(null, true);
top.fullChanged();
}; };
BlockMorph.prototype.unringify = function () { BlockMorph.prototype.unringify = function () {
// remove a Ring around me, if any // remove a Ring around me, if any
var ring = this.parentThatIsA(RingMorph), var ring = this.parentThatIsA(RingMorph),
top = this.topBlock(),
scripts = this.parentThatIsA(ScriptsMorph), scripts = this.parentThatIsA(ScriptsMorph),
block, block,
center; center;
@ -2476,6 +2485,7 @@ BlockMorph.prototype.unringify = function () {
block = ring.contents(); block = ring.contents();
center = ring.center(); center = ring.center();
top.fullChanged();
if (ring.parent instanceof SyntaxElementMorph) { if (ring.parent instanceof SyntaxElementMorph) {
if (block instanceof ReporterBlockMorph) { if (block instanceof ReporterBlockMorph) {
ring.parent.silentReplaceInput(ring, block); ring.parent.silentReplaceInput(ring, block);
@ -2491,6 +2501,7 @@ BlockMorph.prototype.unringify = function () {
ring.destroy(); ring.destroy();
} }
this.fixBlockColor(null, true); this.fixBlockColor(null, true);
top.fullChanged();
}; };
BlockMorph.prototype.relabel = function (alternativeSelectors) { BlockMorph.prototype.relabel = function (alternativeSelectors) {
@ -2887,12 +2898,13 @@ BlockMorph.prototype.eraseHoles = function (context) {
var w = hole.width(), var w = hole.width(),
h = Math.floor(hole.height()) - 2; // Opera needs this h = Math.floor(hole.height()) - 2; // Opera needs this
context.clearRect( context.clearRect(
Math.floor(hole.bounds.origin.x - myself.bounds.origin.x) + 1, hole.bounds.origin.x - myself.bounds.origin.x + 1,
Math.floor(hole.bounds.origin.y - myself.bounds.origin.y) + 1, hole.bounds.origin.y - myself.bounds.origin.y + 1,
isReporter ? w - 1 : w + 1, isReporter ? w - 2 : w + 1,
h h
); );
}); });
}; };
// BlockMorph highlighting // BlockMorph highlighting
@ -3081,7 +3093,7 @@ BlockMorph.prototype.fixBlockColor = function (nearestBlock, isForced) {
BlockMorph.prototype.forceNormalColoring = function () { BlockMorph.prototype.forceNormalColoring = function () {
var clr = SpriteMorph.prototype.blockColor[this.category]; var clr = SpriteMorph.prototype.blockColor[this.category];
this.setColor(clr); this.setColor(clr, true); // silently
this.setLabelColor( this.setLabelColor(
new Color(255, 255, 255), new Color(255, 255, 255),
clr.darker(this.labelContrast), clr.darker(this.labelContrast),
@ -3096,10 +3108,11 @@ BlockMorph.prototype.alternateBlockColor = function () {
if (this.color.eq(clr)) { if (this.color.eq(clr)) {
this.setColor( this.setColor(
this.zebraContrast < 0 ? clr.darker(Math.abs(this.zebraContrast)) this.zebraContrast < 0 ? clr.darker(Math.abs(this.zebraContrast))
: clr.lighter(this.zebraContrast) : clr.lighter(this.zebraContrast),
true // silently
); );
} else { } else {
this.setColor(clr); this.setColor(clr, true); // silently
} }
this.fixLabelColor(); this.fixLabelColor();
this.fixChildrensBlockColor(true); // has issues if not forced this.fixChildrensBlockColor(true); // has issues if not forced
@ -3356,6 +3369,7 @@ BlockMorph.prototype.prepareToBeGrabbed = function (hand) {
}; };
BlockMorph.prototype.justDropped = function () { BlockMorph.prototype.justDropped = function () {
this.alpha = 1;
this.allComments().forEach(function (comment) { this.allComments().forEach(function (comment) {
comment.stopFollowing(); comment.stopFollowing();
}); });
@ -3444,9 +3458,9 @@ function CommandBlockMorph() {
this.init(); this.init();
} }
CommandBlockMorph.prototype.init = function () { CommandBlockMorph.prototype.init = function (silently) {
CommandBlockMorph.uber.init.call(this); CommandBlockMorph.uber.init.call(this, silently);
this.setExtent(new Point(200, 100)); this.setExtent(new Point(200, 100), silently);
this.partOfCustomCommand = false; this.partOfCustomCommand = false;
this.exitTag = null; this.exitTag = null;
}; };
@ -3479,7 +3493,12 @@ CommandBlockMorph.prototype.nextBlock = function (block) {
if (nb) { if (nb) {
block.bottomBlock().nextBlock(nb); block.bottomBlock().nextBlock(nb);
} }
this.fixLayout(); block.setPosition(
new Point(
this.left(),
this.bottom() - (this.corner)
)
);
if (affected) { if (affected) {
affected.fixLayout(); affected.fixLayout();
} }
@ -4125,7 +4144,7 @@ function HatBlockMorph() {
} }
HatBlockMorph.prototype.init = function () { HatBlockMorph.prototype.init = function () {
HatBlockMorph.uber.init.call(this); HatBlockMorph.uber.init.call(this, true); // silently
this.setExtent(new Point(300, 150)); this.setExtent(new Point(300, 150));
}; };
@ -4302,10 +4321,10 @@ function ReporterBlockMorph(isPredicate) {
this.init(isPredicate); this.init(isPredicate);
} }
ReporterBlockMorph.prototype.init = function (isPredicate) { ReporterBlockMorph.prototype.init = function (isPredicate, silently) {
ReporterBlockMorph.uber.init.call(this); ReporterBlockMorph.uber.init.call(this, silently);
this.isPredicate = isPredicate || false; this.isPredicate = isPredicate || false;
this.setExtent(new Point(200, 80)); this.setExtent(new Point(200, 80), silently);
}; };
// ReporterBlockMorph drag & drop: // ReporterBlockMorph drag & drop:
@ -4313,6 +4332,7 @@ ReporterBlockMorph.prototype.init = function (isPredicate) {
ReporterBlockMorph.prototype.snap = function (hand) { ReporterBlockMorph.prototype.snap = function (hand) {
// passing the hand is optional (for when blocks are dragged & dropped) // passing the hand is optional (for when blocks are dragged & dropped)
var scripts = this.parent, var scripts = this.parent,
nb,
target; target;
if (!scripts instanceof ScriptsMorph) { if (!scripts instanceof ScriptsMorph) {
@ -4329,6 +4349,16 @@ ReporterBlockMorph.prototype.snap = function (hand) {
if (target instanceof MultiArgMorph) { if (target instanceof MultiArgMorph) {
scripts.lastPreservedBlocks = target.inputs(); scripts.lastPreservedBlocks = target.inputs();
scripts.lastReplacedInput = target.fullCopy(); scripts.lastReplacedInput = target.fullCopy();
} else if (target instanceof CommandSlotMorph) {
scripts.lastReplacedInput = target;
nb = target.nestedBlock();
if (nb) {
nb = nb.fullCopy();
scripts.add(nb);
nb.moveBy(nb.extent());
nb.fixBlockColor();
scripts.lastPreservedBlocks = [nb];
}
} }
target.parent.replaceInput(target, this); target.parent.replaceInput(target, this);
if (this.snapSound) { if (this.snapSound) {
@ -4352,6 +4382,7 @@ ReporterBlockMorph.prototype.prepareToBeGrabbed = function (handMorph) {
this.setPosition(oldPos); this.setPosition(oldPos);
} }
ReporterBlockMorph.uber.prepareToBeGrabbed.call(this, handMorph); ReporterBlockMorph.uber.prepareToBeGrabbed.call(this, handMorph);
this.alpha = 0.85;
}; };
// ReporterBlockMorph enumerating // ReporterBlockMorph enumerating
@ -4413,7 +4444,7 @@ ReporterBlockMorph.prototype.mouseClickLeft = function (pos) {
this.parent.parent.parent instanceof RingMorph; this.parent.parent.parent instanceof RingMorph;
new DialogBoxMorph( new DialogBoxMorph(
this, this,
this.setSpec, this.userSetSpec,
this this
).prompt( ).prompt(
isRing ? "Input name" : "Script variable name", isRing ? "Input name" : "Script variable name",
@ -4987,6 +5018,7 @@ ScriptsMorph.prototype.init = function (owner) {
ScriptsMorph.uber.init.call(this); ScriptsMorph.uber.init.call(this);
this.setColor(new Color(70, 70, 70)); this.setColor(new Color(70, 70, 70));
this.noticesTransparentClick = true;
}; };
// ScriptsMorph deep copying: // ScriptsMorph deep copying:
@ -5180,14 +5212,15 @@ ScriptsMorph.prototype.closestInput = function (reporter, hand) {
all, all,
function (input) { function (input) {
return (input instanceof InputSlotMorph return (input instanceof InputSlotMorph
|| input instanceof ArgMorph || (input instanceof ArgMorph
&& !(input instanceof CommandSlotMorph)
&& !(input instanceof MultiArgMorph))
|| (input instanceof RingMorph || (input instanceof RingMorph
&& !input.contents()) && !input.contents())
|| input.isEmptySlot()) || input.isEmptySlot())
&& !input.isLocked() && !input.isLocked()
&& input.bounds.containsPoint(handPos) && input.bounds.containsPoint(handPos)
&& !contains(blackList, input) && !contains(blackList, input);
&& touchingVariadicArrowsIfAny(input, handPos);
} }
); );
if (target) { if (target) {
@ -5550,12 +5583,12 @@ function ArgMorph(type) {
this.init(type); this.init(type);
} }
ArgMorph.prototype.init = function (type) { ArgMorph.prototype.init = function (type, silently) {
this.type = type || null; this.type = type || null;
this.isHole = false; this.isHole = false;
ArgMorph.uber.init.call(this); ArgMorph.uber.init.call(this, silently);
this.color = new Color(0, 17, 173); this.color = new Color(0, 17, 173);
this.setExtent(new Point(50, 50)); this.setExtent(new Point(50, 50), silently);
}; };
// ArgMorph drag & drop: for demo puposes only // ArgMorph drag & drop: for demo puposes only
@ -5663,10 +5696,13 @@ function CommandSlotMorph() {
this.init(); this.init();
} }
CommandSlotMorph.prototype.init = function () { CommandSlotMorph.prototype.init = function (silently) {
CommandSlotMorph.uber.init.call(this); CommandSlotMorph.uber.init.call(this, null, true); // silently
this.color = new Color(0, 17, 173); this.color = new Color(0, 17, 173);
this.setExtent(new Point(230, this.corner * 4 + this.cSlotPadding)); this.setExtent(
new Point(230, this.corner * 4 + this.cSlotPadding),
silently
);
}; };
CommandSlotMorph.prototype.getSpec = function () { CommandSlotMorph.prototype.getSpec = function () {
@ -6118,8 +6154,8 @@ function RingCommandSlotMorph() {
this.init(); this.init();
} }
RingCommandSlotMorph.prototype.init = function () { RingCommandSlotMorph.prototype.init = function (silently) {
RingCommandSlotMorph.uber.init.call(this); RingCommandSlotMorph.uber.init.call(this, silently);
this.isHole = true; this.isHole = true;
this.noticesTransparentClick = true; this.noticesTransparentClick = true;
this.color = new Color(0, 17, 173); this.color = new Color(0, 17, 173);
@ -6269,11 +6305,15 @@ function CSlotMorph() {
this.init(); this.init();
} }
CSlotMorph.prototype.init = function () { CSlotMorph.prototype.init = function (silently) {
CommandSlotMorph.uber.init.call(this); CommandSlotMorph.uber.init.call(this, null, true); // silently
this.isHole = true; this.isHole = true;
this.isLambda = false; // see Process.prototype.evaluateInput
this.color = new Color(0, 17, 173); this.color = new Color(0, 17, 173);
this.setExtent(new Point(230, this.corner * 4 + this.cSlotPadding)); this.setExtent(
new Point(230, this.corner * 4 + this.cSlotPadding),
silently
);
}; };
CSlotMorph.prototype.getSpec = function () { CSlotMorph.prototype.getSpec = function () {
@ -6721,7 +6761,7 @@ InputSlotMorph.prototype.init = function (
this.minWidth = 0; // can be chaged for text-type inputs ("landscape") this.minWidth = 0; // can be chaged for text-type inputs ("landscape")
this.constant = null; this.constant = null;
InputSlotMorph.uber.init.call(this); InputSlotMorph.uber.init.call(this, null, true);
this.color = new Color(255, 255, 255); this.color = new Color(255, 255, 255);
this.add(contents); this.add(contents);
this.add(arrow); this.add(arrow);
@ -7874,7 +7914,7 @@ ArrowMorph.prototype.init = function (direction, size, padding, color) {
this.size = size || ((size === 0) ? 0 : 50); this.size = size || ((size === 0) ? 0 : 50);
this.padding = padding || 0; this.padding = padding || 0;
ArrowMorph.uber.init.call(this); ArrowMorph.uber.init.call(this, true); // silently
this.color = color || new Color(0, 0, 0); this.color = color || new Color(0, 0, 0);
this.setExtent(new Point(this.size, this.size)); this.setExtent(new Point(this.size, this.size));
}; };
@ -7963,7 +8003,7 @@ TextSlotMorph.prototype.init = function (
this.minWidth = 0; // can be chaged for text-type inputs ("landscape") this.minWidth = 0; // can be chaged for text-type inputs ("landscape")
this.constant = null; this.constant = null;
InputSlotMorph.uber.init.call(this); InputSlotMorph.uber.init.call(this, null, null, null, null, true); // sil.
this.color = new Color(255, 255, 255); this.color = new Color(255, 255, 255);
this.add(contents); this.add(contents);
this.add(arrow); this.add(arrow);
@ -8084,7 +8124,7 @@ SymbolMorph.prototype.init = function (
this.shadowOffset = shadowOffset || new Point(0, 0); this.shadowOffset = shadowOffset || new Point(0, 0);
this.shadowColor = shadowColor || null; this.shadowColor = shadowColor || null;
SymbolMorph.uber.init.call(this); SymbolMorph.uber.init.call(this, true); // silently
this.color = color || new Color(0, 0, 0); this.color = color || new Color(0, 0, 0);
this.drawNew(); this.drawNew();
}; };
@ -9316,7 +9356,7 @@ function ColorSlotMorph(clr) {
} }
ColorSlotMorph.prototype.init = function (clr) { ColorSlotMorph.prototype.init = function (clr) {
ColorSlotMorph.uber.init.call(this); ColorSlotMorph.uber.init.call(this, null, true); // silently
this.setColor(clr || new Color(145, 26, 68)); this.setColor(clr || new Color(145, 26, 68));
}; };
@ -9494,7 +9534,7 @@ MultiArgMorph.prototype.init = function (
this.shadowOffset = shadowOffset || null; this.shadowOffset = shadowOffset || null;
this.canBeEmpty = true; this.canBeEmpty = true;
MultiArgMorph.uber.init.call(this); MultiArgMorph.uber.init.call(this, null, true); // silently
// MultiArgMorphs are transparent by default b/c of zebra coloring // MultiArgMorphs are transparent by default b/c of zebra coloring
this.alpha = isTransparent === false ? 1 : 0; this.alpha = isTransparent === false ? 1 : 0;
@ -9882,7 +9922,7 @@ ArgLabelMorph.prototype.init = function (argMorph, labelTxt) {
var label; var label;
this.labelText = localize(labelTxt || 'input list:'); this.labelText = localize(labelTxt || 'input list:');
ArgLabelMorph.uber.init.call(this); ArgLabelMorph.uber.init.call(this, null, true); // silently
this.isStatic = true; // I cannot be exchanged this.isStatic = true; // I cannot be exchanged
@ -10008,14 +10048,17 @@ function FunctionSlotMorph(isPredicate) {
this.init(isPredicate); this.init(isPredicate);
} }
FunctionSlotMorph.prototype.init = function (isPredicate) { FunctionSlotMorph.prototype.init = function (isPredicate, silently) {
FunctionSlotMorph.uber.init.call(this); FunctionSlotMorph.uber.init.call(this, null, true); // silently
this.isPredicate = isPredicate || false; this.isPredicate = isPredicate || false;
this.color = this.rfColor; this.color = this.rfColor;
this.setExtent(new Point( this.setExtent(
(this.fontSize + this.edge * 2) * 2, new Point(
this.fontSize + this.edge * 2 (this.fontSize + this.edge * 2) * 2,
)); this.fontSize + this.edge * 2
),
silently
);
}; };
FunctionSlotMorph.prototype.getSpec = function () { FunctionSlotMorph.prototype.getSpec = function () {
@ -10390,7 +10433,7 @@ function ReporterSlotMorph(isPredicate) {
} }
ReporterSlotMorph.prototype.init = function (isPredicate) { ReporterSlotMorph.prototype.init = function (isPredicate) {
ReporterSlotMorph.uber.init.call(this, isPredicate); ReporterSlotMorph.uber.init.call(this, isPredicate, true);
this.add(this.emptySlot()); this.add(this.emptySlot());
this.fixLayout(); this.fixLayout();
}; };
@ -10481,7 +10524,7 @@ function RingReporterSlotMorph(isPredicate) {
} }
RingReporterSlotMorph.prototype.init = function (isPredicate) { RingReporterSlotMorph.prototype.init = function (isPredicate) {
RingReporterSlotMorph.uber.init.call(this, isPredicate); RingReporterSlotMorph.uber.init.call(this, isPredicate, true);
this.alpha = RingMorph.prototype.alpha; this.alpha = RingMorph.prototype.alpha;
this.contrast = RingMorph.prototype.contrast; this.contrast = RingMorph.prototype.contrast;
this.isHole = true; this.isHole = true;

58
byob.js
Wyświetl plik

@ -108,7 +108,7 @@ SymbolMorph, isNil, CursorMorph*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.byob = '2015-October-07'; modules.byob = '2015-November-16';
// Declarations // Declarations
@ -148,6 +148,7 @@ function CustomBlockDefinition(spec, receiver) {
// don't serialize (not needed for functionality): // don't serialize (not needed for functionality):
this.receiver = receiver || null; // for serialization only (pointer) this.receiver = receiver || null; // for serialization only (pointer)
this.editorDimensions = null; // a rectangle, last bounds of the editor
} }
// CustomBlockDefinition instantiating blocks // CustomBlockDefinition instantiating blocks
@ -405,9 +406,7 @@ function CustomCommandBlockMorph(definition, isProto) {
CustomCommandBlockMorph.prototype.init = function (definition, isProto) { CustomCommandBlockMorph.prototype.init = function (definition, isProto) {
this.definition = definition; // mandatory this.definition = definition; // mandatory
this.isPrototype = isProto || false; // optional this.isPrototype = isProto || false; // optional
CustomCommandBlockMorph.uber.init.call(this, true); // silently
CustomCommandBlockMorph.uber.init.call(this);
this.category = definition.category; this.category = definition.category;
this.selector = 'evaluateCustomBlock'; this.selector = 'evaluateCustomBlock';
if (definition) { // needed for de-serializing if (definition) { // needed for de-serializing
@ -415,7 +414,7 @@ CustomCommandBlockMorph.prototype.init = function (definition, isProto) {
} }
}; };
CustomCommandBlockMorph.prototype.refresh = function () { CustomCommandBlockMorph.prototype.refresh = function (silently) {
var def = this.definition, var def = this.definition,
newSpec = this.isPrototype ? newSpec = this.isPrototype ?
def.spec : def.blockSpec(), def.spec : def.blockSpec(),
@ -429,7 +428,7 @@ CustomCommandBlockMorph.prototype.refresh = function () {
} else { } else {
this.fixBlockColor(); this.fixBlockColor();
} }
this.setSpec(newSpec); this.setSpec(newSpec, silently);
this.fixLabelColor(); this.fixLabelColor();
this.restoreInputs(oldInputs); this.restoreInputs(oldInputs);
} else { // update all input slots' drop-downs } else { // update all input slots' drop-downs
@ -671,7 +670,7 @@ CustomCommandBlockMorph.prototype.mouseClickLeft = function () {
}; };
CustomCommandBlockMorph.prototype.edit = function () { CustomCommandBlockMorph.prototype.edit = function () {
var myself = this, block, hat; var myself = this, editor, block, hat;
if (this.isPrototype) { if (this.isPrototype) {
block = this.definition.blockInstance(); block = this.definition.blockInstance();
@ -696,7 +695,11 @@ CustomCommandBlockMorph.prototype.edit = function () {
myself.isInUse() myself.isInUse()
); );
} else { } else {
new BlockEditorMorph(this.definition, this.receiver()).popUp(); Morph.prototype.trackChanges = false;
editor = new BlockEditorMorph(this.definition, this.receiver());
editor.popUp();
Morph.prototype.trackChanges = true;
editor.changed();
} }
}; };
@ -763,7 +766,7 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
menu.addItem( menu.addItem(
"script pic...", "script pic...",
function () { function () {
window.open(this.topBlock().fullImage().toDataURL()); window.open(this.topBlock().scriptPic().toDataURL());
}, },
'open a new window\nwith a picture of this script' 'open a new window\nwith a picture of this script'
); );
@ -936,7 +939,7 @@ CustomReporterBlockMorph.prototype.init = function (
this.definition = definition; // mandatory this.definition = definition; // mandatory
this.isPrototype = isProto || false; // optional this.isPrototype = isProto || false; // optional
CustomReporterBlockMorph.uber.init.call(this, isPredicate); CustomReporterBlockMorph.uber.init.call(this, isPredicate, true); // sil.
this.category = definition.category; this.category = definition.category;
this.selector = 'evaluateCustomBlock'; this.selector = 'evaluateCustomBlock';
@ -946,7 +949,7 @@ CustomReporterBlockMorph.prototype.init = function (
}; };
CustomReporterBlockMorph.prototype.refresh = function () { CustomReporterBlockMorph.prototype.refresh = function () {
CustomCommandBlockMorph.prototype.refresh.call(this); CustomCommandBlockMorph.prototype.refresh.call(this, true);
if (!this.isPrototype) { if (!this.isPrototype) {
this.isPredicate = (this.definition.type === 'predicate'); this.isPredicate = (this.definition.type === 'predicate');
} }
@ -1726,7 +1729,7 @@ BlockEditorMorph.prototype.init = function (definition, target) {
this.addButton('updateDefinition', 'Apply'); this.addButton('updateDefinition', 'Apply');
this.addButton('cancel', 'Cancel'); this.addButton('cancel', 'Cancel');
this.setExtent(new Point(375, 300)); this.setExtent(new Point(375, 300)); // normal initial extent
this.fixLayout(); this.fixLayout();
proto.children[0].fixLayout(); proto.children[0].fixLayout();
scripts.fixMultiArgs(); scripts.fixMultiArgs();
@ -1737,6 +1740,7 @@ BlockEditorMorph.prototype.popUp = function () {
if (world) { if (world) {
BlockEditorMorph.uber.popUp.call(this, world); BlockEditorMorph.uber.popUp.call(this, world);
this.setInitialDimensions();
this.handle = new HandleMorph( this.handle = new HandleMorph(
this, this,
280, 280,
@ -1744,9 +1748,18 @@ BlockEditorMorph.prototype.popUp = function () {
this.corner, this.corner,
this.corner this.corner
); );
world.keyboardReceiver = null;
} }
}; };
BlockEditorMorph.prototype.justDropped = function () {
// override the inherited default behavior, which is to
// give keyboard focus to dialog boxes, as in this case
// we want Snap-global keyboard-shortcuts like ctrl-f
// to still work
nop();
};
// BlockEditorMorph ops // BlockEditorMorph ops
BlockEditorMorph.prototype.accept = function (origin) { BlockEditorMorph.prototype.accept = function (origin) {
@ -1851,6 +1864,7 @@ BlockEditorMorph.prototype.updateDefinition = function () {
this.definition.spec = this.prototypeSpec(); this.definition.spec = this.prototypeSpec();
this.definition.declarations = this.prototypeSlots(); this.definition.declarations = this.prototypeSlots();
this.definition.scripts = []; this.definition.scripts = [];
this.definition.editorDimensions = this.bounds.copy();
this.body.contents.children.forEach(function (morph) { this.body.contents.children.forEach(function (morph) {
if (morph instanceof PrototypeHatBlockMorph) { if (morph instanceof PrototypeHatBlockMorph) {
@ -1927,6 +1941,26 @@ BlockEditorMorph.prototype.prototypeSlots = function () {
// BlockEditorMorph layout // BlockEditorMorph layout
BlockEditorMorph.prototype.setInitialDimensions = function () {
var world = this.world(),
mex = world.extent().subtract(new Point(this.padding, this.padding)),
th = fontHeight(this.titleFontSize) + this.titlePadding * 2,
bh = this.buttons.height();
if (this.definition.editorDimensions) {
this.setPosition(this.definition.editorDimensions.origin);
this.setExtent(this.definition.editorDimensions.extent().min(mex));
this.keepWithin(world);
return;
}
this.setExtent(
this.body.contents.extent().add(
new Point(this.padding, this.padding + th + bh)
).min(mex)
);
this.setCenter(this.world().center());
};
BlockEditorMorph.prototype.fixLayout = function () { BlockEditorMorph.prototype.fixLayout = function () {
var th = fontHeight(this.titleFontSize) + this.titlePadding * 2; var th = fontHeight(this.titleFontSize) + this.titlePadding * 2;

41
gui.js
Wyświetl plik

@ -71,7 +71,7 @@ BlockRemovalDialogMorph*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.gui = '2015-October-07'; modules.gui = '2015-November-16';
// Declarations // Declarations
@ -2227,6 +2227,15 @@ IDE_Morph.prototype.settingsMenu = function () {
'Stage size...', 'Stage size...',
'userSetStageSize' 'userSetStageSize'
); );
if (shiftClicked) {
menu.addItem(
'Dragging threshold...',
'userSetDragThreshold',
'specify the distance the hand has to move\n' +
'before it picks up an object',
new Color(100, 0, 0)
);
}
menu.addLine(); menu.addLine();
addPreference( addPreference(
'Blurred shadows', 'Blurred shadows',
@ -2713,7 +2722,7 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn, module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
world = this.world(); world = this.world();
aboutTxt = 'Snap! 4.0.2\nBuild Your Own Blocks\n\n' aboutTxt = 'Snap! 4.0.3\nBuild Your Own Blocks\n\n'
+ 'Copyright \u24B8 2015 Jens M\u00F6nig and ' + 'Copyright \u24B8 2015 Jens M\u00F6nig and '
+ 'Brian Harvey\n' + 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n' + 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
@ -4201,6 +4210,29 @@ IDE_Morph.prototype.setStageExtent = function (aPoint) {
} }
}; };
// IDE_Morph dragging threshold (internal feature)
IDE_Morph.prototype.userSetDragThreshold = function () {
new DialogBoxMorph(
this,
function (num) {
MorphicPreferences.grabThreshold = Math.min(
Math.max(+num, 0),
200
);
},
this
).prompt(
"Dragging threshold",
MorphicPreferences.grabThreshold.toString(),
this.world(),
null, // pic
null, // choices
null, // read only
true // numeric
);
};
// IDE_Morph cloud interface // IDE_Morph cloud interface
IDE_Morph.prototype.initializeCloud = function () { IDE_Morph.prototype.initializeCloud = function () {
@ -5945,6 +5977,7 @@ SpriteIconMorph.prototype.prepareToBeGrabbed = function () {
var ide = this.parentThatIsA(IDE_Morph), var ide = this.parentThatIsA(IDE_Morph),
idx; idx;
this.mouseClickLeft(); // select me this.mouseClickLeft(); // select me
this.alpha = 0.85;
if (ide) { if (ide) {
idx = ide.sprites.asArray().indexOf(this.object); idx = ide.sprites.asArray().indexOf(this.object);
ide.sprites.remove(idx + 1); ide.sprites.remove(idx + 1);
@ -5953,6 +5986,10 @@ SpriteIconMorph.prototype.prepareToBeGrabbed = function () {
} }
}; };
SpriteIconMorph.prototype.justDropped = function () {
this.alpha = 1;
};
SpriteIconMorph.prototype.wantsDropOf = function (morph) { SpriteIconMorph.prototype.wantsDropOf = function (morph) {
// allow scripts & media to be copied from one sprite to another // allow scripts & media to be copied from one sprite to another
// by drag & drop // by drag & drop

Wyświetl plik

@ -2603,3 +2603,75 @@ ______
* Store: Fix deserialization support for projects using inheritance * Store: Fix deserialization support for projects using inheritance
* German translation update * German translation update
++++++++++++++++++++++++++
new stuff - bulk of 151116
++++++++++++++++++++++++++
151030
------
* Blocks: Tweak precision of rendering of transparent “holes”
* updated Czech translation (remember to integrate from email!)
* Morphic: Streamlined nop-stepping
* Blocks: Let SyntaxElements step (again), for better input slot editing experience
151101
------
* BYOB: Script pic: Always export comments attached to custom block definitions
* Blocks: fixed #982 (made %interaction slot static)
* Morphic: removed an obsolete line (“dragOrigin”)
* BYOB: make block editor big enough to show the whole definition, if possible
* BYOB: remember user-set position and size of block editor when pressing “OK”, per session (not serialized in project)
* Blocks: speed up stacking of commands (also when done programmatically) by suppressing redraws
* Morphic, Blocks, BYOB: Suppress redundant redraws
151104
------
* Morphic: new grabTheshold preference to suppress accidental grabbing through micro-movements of the hand
* GUI: hidden (shift-click) option to adjust the grabThreshold for the current session
* Lists, Blocks: Expand list watchers inside result bubbles to show everything
* Objects: Expand list watchers inside speech/thought bubbles to show everything
* Morphic: fixed a bug that occasionally expanded the Hands bounds when dragging morphs
151107
------
* Threads: invoke a block synchronously
151009
------
* Morphic: cache fullImage and fullBounds when dragging
* Blocks: make reporters semi-transparent while dragging
* GUI: make SpriteIcons semi-transparent while dragging
* Blocks: make it harder to drop reporters onto filled custom C-slots and variadic slot arrows
* Blocks: make ScriptsMorphs notice transparent clicks (addresses #997)
* Blocks: fixed “undrop” for replacing C-slots with reporters
* BYOB: fixed ctrl-f for the BlockEditor in all situations
151111
------
* Objects: fixed a between slideBackTo() and possible running scripts in sprites, thanks, Paul, for reporting it!
151112
------
* Blocks, Objects, Threads: new internal slot type: %cl for auto-reifying C-slots that reject reporter drops. Changed (hidden) “for each” to reject reporters in C-slot
151113
------
* Frames, snap.html: initial version of a new general purpose prototypal single inheritance object system
* snap_slo.html: alternative animation-frame based outer scheduler, experimental
* Threads: added optional timeout to the new synchronous invoke(block) function
* Blocks: fixed too brutally optimized redraw for “ringify” and “unringify”
151114
------
* Frames, snap.html, snap_slo.html: remove initial version for now, needs more low-levelish rewrite (Map-based “shortcut” design doesnt cut it).
151116
------
* Blocks, GUI: Slightly less transparency for dragged reporters and sprite icons
=== v4.0.3 (unreleased) ===
++++++++++++++++++++++++++
end - bulk of 151116
++++++++++++++++++++++++++

Wyświetl plik

@ -61,7 +61,7 @@ PushButtonMorph, SyntaxElementMorph, Color, Point, WatcherMorph,
StringMorph, SpriteMorph, ScrollFrameMorph, CellMorph, ArrowMorph, StringMorph, SpriteMorph, ScrollFrameMorph, CellMorph, ArrowMorph,
MenuMorph, snapEquals, Morph, isNil, localize, MorphicPreferences*/ MenuMorph, snapEquals, Morph, isNil, localize, MorphicPreferences*/
modules.lists = '2015-October-07'; modules.lists = '2015-November-16';
var List; var List;
var ListWatcherMorph; var ListWatcherMorph;
@ -682,13 +682,14 @@ ListWatcherMorph.prototype.arrangeCells = function () {
this.frame.contents.adjustBounds(); this.frame.contents.adjustBounds();
}; };
ListWatcherMorph.prototype.expand = function () { ListWatcherMorph.prototype.expand = function (maxExtent) {
// make sure to show all (first 100) cells // make sure to show all (first 100) cells
// used for exporting a project summary
var fe = this.frame.contents.extent(), var fe = this.frame.contents.extent(),
w = fe.x + 6, ext = new Point(fe.x + 6, fe.y + this.label.height() + 6);
h = fe.y + this.label.height() + 6; if (maxExtent) {
this.setExtent(new Point(w, h)); ext = ext.min(maxExtent);
}
this.setExtent(ext);
this.handle.setRight(this.right() - 3); this.handle.setRight(this.right() - 3);
this.handle.setBottom(this.bottom() - 3); this.handle.setBottom(this.bottom() - 3);
}; };

Wyświetl plik

@ -1052,7 +1052,7 @@
/*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio, /*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio,
FileList, getBlurredShadowSupport*/ FileList, getBlurredShadowSupport*/
var morphicVersion = '2015-September-23'; var morphicVersion = '2015-November-16';
var modules = {}; // keep track of additional loaded modules var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@ -1072,7 +1072,8 @@ var standardSettings = {
useVirtualKeyboard: true, useVirtualKeyboard: true,
isTouchDevice: false, // turned on by touch events, don't set isTouchDevice: false, // turned on by touch events, don't set
rasterizeSVGs: false, rasterizeSVGs: false,
isFlat: false isFlat: false,
grabThreshold: 5
}; };
var touchScreenSettings = { var touchScreenSettings = {
@ -1091,7 +1092,8 @@ var touchScreenSettings = {
useVirtualKeyboard: true, useVirtualKeyboard: true,
isTouchDevice: false, isTouchDevice: false,
rasterizeSVGs: false, rasterizeSVGs: false,
isFlat: false isFlat: false,
grabThreshold: 5
}; };
var MorphicPreferences = standardSettings; var MorphicPreferences = standardSettings;
@ -2207,7 +2209,10 @@ function Morph() {
Morph.prototype.init = function (noDraw) { Morph.prototype.init = function (noDraw) {
Morph.uber.init.call(this); Morph.uber.init.call(this);
this.isMorph = true; this.isMorph = true;
this.image = null;
this.bounds = new Rectangle(0, 0, 50, 40); this.bounds = new Rectangle(0, 0, 50, 40);
this.cachedFullImage = null;
this.cachedFullBounds = null;
this.color = new Color(80, 80, 80); this.color = new Color(80, 80, 80);
this.texture = null; // optional url of a fill-image this.texture = null; // optional url of a fill-image
this.cachedTexture = null; // internal cache of actual bg image this.cachedTexture = null; // internal cache of actual bg image
@ -2284,9 +2289,7 @@ Morph.prototype.nextSteps = function (arrayOfFunctions) {
} }
}; };
Morph.prototype.step = function () { Morph.prototype.step = nop;
nop();
};
// Morph accessing - geometry getting: // Morph accessing - geometry getting:
@ -2412,6 +2415,9 @@ Morph.prototype.silentMoveBy = function (delta) {
var children = this.children, var children = this.children,
i = children.length; i = children.length;
this.bounds = this.bounds.translateBy(delta); this.bounds = this.bounds.translateBy(delta);
if (this.cachedFullBounds) {
this.cachedFullBounds = this.cachedFullBounds.translateBy(delta);
}
// ugly optimization avoiding forEach() // ugly optimization avoiding forEach()
for (i; i > 0; i -= 1) { for (i; i > 0; i -= 1) {
children[i - 1].silentMoveBy(delta); children[i - 1].silentMoveBy(delta);
@ -2533,7 +2539,12 @@ Morph.prototype.scrollIntoView = function () {
// Morph accessing - dimensional changes requiring a complete redraw // Morph accessing - dimensional changes requiring a complete redraw
Morph.prototype.setExtent = function (aPoint) { Morph.prototype.setExtent = function (aPoint, silently) {
// silently avoids redrawing the receiver
if (silently) {
this.silentSetExtent(aPoint);
return;
}
if (!aPoint.eq(this.extent())) { if (!aPoint.eq(this.extent())) {
this.changed(); this.changed();
this.silentSetExtent(aPoint); this.silentSetExtent(aPoint);
@ -2640,29 +2651,31 @@ Morph.prototype.drawCachedTexture = function () {
*/ */
Morph.prototype.drawOn = function (aCanvas, aRect) { Morph.prototype.drawOn = function (aCanvas, aRect) {
var rectangle, area, delta, src, context, w, h, sl, st; var rectangle, area, delta, src, context, w, h, sl, st,
pic = this.cachedFullImage || this.image,
bounds = this.cachedFullBounds || this.bounds;
if (!this.isVisible) { if (!this.isVisible) {
return null; return null;
} }
rectangle = aRect || this.bounds(); rectangle = aRect || bounds;
area = rectangle.intersect(this.bounds); area = rectangle.intersect(bounds);
if (area.extent().gt(new Point(0, 0))) { if (area.extent().gt(new Point(0, 0))) {
delta = this.position().neg(); delta = bounds.position().neg();
src = area.copy().translateBy(delta); src = area.copy().translateBy(delta);
context = aCanvas.getContext('2d'); context = aCanvas.getContext('2d');
context.globalAlpha = this.alpha; context.globalAlpha = this.alpha;
sl = src.left(); sl = src.left();
st = src.top(); st = src.top();
w = Math.min(src.width(), this.image.width - sl); w = Math.min(src.width(), pic.width - sl);
h = Math.min(src.height(), this.image.height - st); h = Math.min(src.height(), pic.height - st);
if (w < 1 || h < 1) { if (w < 1 || h < 1) {
return null; return null;
} }
context.drawImage( context.drawImage(
this.image, pic,
sl, sl,
st, st,
w, w,
@ -2672,42 +2685,6 @@ Morph.prototype.drawOn = function (aCanvas, aRect) {
w, w,
h h
); );
/* "for debugging purposes:"
try {
context.drawImage(
this.image,
sl,
st,
w,
h,
area.left(),
area.top(),
w,
h
);
} catch (err) {
alert('internal error\n\n' + err
+ '\n ---'
+ '\n canvas: ' + aCanvas
+ '\n canvas.width: ' + aCanvas.width
+ '\n canvas.height: ' + aCanvas.height
+ '\n ---'
+ '\n image: ' + this.image
+ '\n image.width: ' + this.image.width
+ '\n image.height: ' + this.image.height
+ '\n ---'
+ '\n w: ' + w
+ '\n h: ' + h
+ '\n sl: ' + sl
+ '\n st: ' + st
+ '\n area.left: ' + area.left()
+ '\n area.top ' + area.top()
);
}
*/
} }
}; };
@ -2716,8 +2693,9 @@ Morph.prototype.fullDrawOn = function (aCanvas, aRect) {
if (!this.isVisible) { if (!this.isVisible) {
return null; return null;
} }
rectangle = aRect || this.fullBounds(); rectangle = aRect || this.cachedFullBounds || this.fullBounds();
this.drawOn(aCanvas, rectangle); this.drawOn(aCanvas, rectangle);
if (this.cachedFullImage) {return; }
this.children.forEach(function (child) { this.children.forEach(function (child) {
child.fullDrawOn(aCanvas, rectangle); child.fullDrawOn(aCanvas, rectangle);
}); });
@ -2911,7 +2889,9 @@ Morph.prototype.fullChanged = function () {
if (this.trackChanges) { if (this.trackChanges) {
var w = this.root(); var w = this.root();
if (w instanceof WorldMorph) { if (w instanceof WorldMorph) {
w.broken.push(this.fullBounds().spread()); w.broken.push(
(this.cachedFullBounds || this.fullBounds()).spread()
);
} }
} }
}; };
@ -9448,7 +9428,7 @@ function HandMorph(aWorld) {
// HandMorph initialization: // HandMorph initialization:
HandMorph.prototype.init = function (aWorld) { HandMorph.prototype.init = function (aWorld) {
HandMorph.uber.init.call(this); HandMorph.uber.init.call(this, true);
this.bounds = new Rectangle(); this.bounds = new Rectangle();
// additional properties: // additional properties:
@ -9456,6 +9436,7 @@ HandMorph.prototype.init = function (aWorld) {
this.mouseButton = null; this.mouseButton = null;
this.mouseOverList = []; this.mouseOverList = [];
this.morphToGrab = null; this.morphToGrab = null;
this.grabPosition = null;
this.grabOrigin = null; this.grabOrigin = null;
this.temporaries = []; this.temporaries = [];
this.touchHoldTimeout = null; this.touchHoldTimeout = null;
@ -9520,6 +9501,8 @@ HandMorph.prototype.grab = function (aMorph) {
if (aMorph.prepareToBeGrabbed) { if (aMorph.prepareToBeGrabbed) {
aMorph.prepareToBeGrabbed(this); aMorph.prepareToBeGrabbed(this);
} }
aMorph.cachedFullImage = aMorph.fullImageClassic();
aMorph.cachedFullBounds = aMorph.fullBounds();
this.add(aMorph); this.add(aMorph);
this.changed(); this.changed();
if (oldParent && oldParent.reactToGrabOf) { if (oldParent && oldParent.reactToGrabOf) {
@ -9535,6 +9518,8 @@ HandMorph.prototype.drop = function () {
target = this.dropTargetFor(morphToDrop); target = this.dropTargetFor(morphToDrop);
this.changed(); this.changed();
target.add(morphToDrop); target.add(morphToDrop);
morphToDrop.cachedFullImage = null;
morphToDrop.cachedFullBounds = null;
morphToDrop.changed(); morphToDrop.changed();
morphToDrop.removeShadow(); morphToDrop.removeShadow();
this.children = []; this.children = [];
@ -9545,7 +9530,6 @@ HandMorph.prototype.drop = function () {
if (target.reactToDropOf) { if (target.reactToDropOf) {
target.reactToDropOf(morphToDrop, this); target.reactToDropOf(morphToDrop, this);
} }
this.dragOrigin = null;
} }
}; };
@ -9572,6 +9556,7 @@ HandMorph.prototype.processMouseDown = function (event) {
this.destroyTemporaries(); this.destroyTemporaries();
this.contextMenuEnabled = true; this.contextMenuEnabled = true;
this.morphToGrab = null; this.morphToGrab = null;
this.grabPosition = null;
if (this.children.length !== 0) { if (this.children.length !== 0) {
this.drop(); this.drop();
this.mouseButton = null; this.mouseButton = null;
@ -9599,6 +9584,7 @@ HandMorph.prototype.processMouseDown = function (event) {
} }
if (!morph.mouseMove) { if (!morph.mouseMove) {
this.morphToGrab = morph.rootForGrab(); this.morphToGrab = morph.rootForGrab();
this.grabPosition = this.bounds.origin.copy();
} }
if (event.button === 2 || event.ctrlKey) { if (event.button === 2 || event.ctrlKey) {
this.mouseButton = 'right'; this.mouseButton = 'right';
@ -9708,8 +9694,7 @@ HandMorph.prototype.processMouseMove = function (event) {
mouseOverNew, mouseOverNew,
myself = this, myself = this,
morph, morph,
topMorph, topMorph;
fb;
pos = new Point( pos = new Point(
event.pageX - posInDocument.x, event.pageX - posInDocument.x,
@ -9733,7 +9718,11 @@ HandMorph.prototype.processMouseMove = function (event) {
} }
// if a morph is marked for grabbing, just grab it // if a morph is marked for grabbing, just grab it
if (this.mouseButton === 'left' && this.morphToGrab) { if (this.mouseButton === 'left' &&
this.morphToGrab &&
(this.grabPosition.distanceTo(this.bounds.origin) >
MorphicPreferences.grabThreshold)) {
this.setPosition(this.grabPosition);
if (this.morphToGrab.isDraggable) { if (this.morphToGrab.isDraggable) {
morph = this.morphToGrab; morph = this.morphToGrab;
this.grab(morph); this.grab(morph);
@ -9747,16 +9736,7 @@ HandMorph.prototype.processMouseMove = function (event) {
this.grab(morph); this.grab(morph);
this.grabOrigin = this.morphToGrab.situation(); this.grabOrigin = this.morphToGrab.situation();
} }
if (morph) { this.setPosition(pos);
// if the mouse has left its fullBounds, allow to center it
fb = morph.fullBounds();
if (!fb.containsPoint(pos) &&
morph.isCorrectingOutsideDrag()) {
this.bounds.origin = fb.center();
this.grab(morph);
this.setPosition(pos);
}
}
} }
} }

Wyświetl plik

@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.objects = '2015-October-07'; modules.objects = '2015-November-16';
var SpriteMorph; var SpriteMorph;
var StageMorph; var StageMorph;
@ -1184,7 +1184,7 @@ SpriteMorph.prototype.initBlocks = function () {
dev: true, dev: true,
type: 'command', type: 'command',
category: 'lists', category: 'lists',
spec: 'for %upvar in %l %cs', spec: 'for %upvar in %l %cl',
defaults: [localize('each item')] defaults: [localize('each item')]
}, },
@ -2326,7 +2326,7 @@ SpriteMorph.prototype.freshPalette = function (category) {
x = block.right() + unit / 2; x = block.right() + unit / 2;
ry = block.bottom(); ry = block.bottom();
} else { } else {
if (block.fixLayout) {block.fixLayout(); } // if (block.fixLayout) {block.fixLayout(); }
x = 0; x = 0;
y += block.height(); y += block.height();
} }
@ -3249,7 +3249,7 @@ SpriteMorph.prototype.bubble = function (data, isThought, isQuestion) {
if (data === '' || isNil(data)) {return; } if (data === '' || isNil(data)) {return; }
bubble = new SpriteBubbleMorph( bubble = new SpriteBubbleMorph(
data, data,
stage ? stage.scale : 1, stage,
isThought, isThought,
isQuestion isQuestion
); );
@ -3539,6 +3539,7 @@ SpriteMorph.prototype.gotoXY = function (x, y, justMe) {
newY, newY,
dest; dest;
if (!stage) {return; }
newX = stage.center().x + (+x || 0) * stage.scale; newX = stage.center().x + (+x || 0) * stage.scale;
newY = stage.center().y - (+y || 0) * stage.scale; newY = stage.center().y - (+y || 0) * stage.scale;
if (this.costume) { if (this.costume) {
@ -6054,18 +6055,19 @@ SpriteBubbleMorph.uber = SpeechBubbleMorph.prototype;
// SpriteBubbleMorph instance creation: // SpriteBubbleMorph instance creation:
function SpriteBubbleMorph(data, scale, isThought, isQuestion) { function SpriteBubbleMorph(data, stage, isThought, isQuestion) {
this.init(data, scale, isThought, isQuestion); this.init(data, stage, isThought, isQuestion);
} }
SpriteBubbleMorph.prototype.init = function ( SpriteBubbleMorph.prototype.init = function (
data, data,
scale, stage,
isThought, isThought,
isQuestion isQuestion
) { ) {
var sprite = SpriteMorph.prototype; var sprite = SpriteMorph.prototype;
this.scale = scale || 1; this.stage = stage;
this.scale = stage ? stage.scale : 1;
this.data = data; this.data = data;
this.isQuestion = isQuestion; this.isQuestion = isQuestion;
@ -6125,6 +6127,11 @@ SpriteBubbleMorph.prototype.dataAsMorph = function (data) {
contents.isDraggable = false; contents.isDraggable = false;
contents.update(true); contents.update(true);
contents.step = contents.update; contents.step = contents.update;
if (this.stage) {
contents.expand(this.stage.extent().translateBy(
-2 * (this.edge + this.border + this.padding)
));
}
} else if (data instanceof Context) { } else if (data instanceof Context) {
img = data.image(); img = data.image();
contents = new Morph(); contents = new Morph();

38
snap_slo.html 100755
Wyświetl plik

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Snap! Build Your Own Blocks</title>
<link rel="shortcut icon" href="favicon.ico">
<script type="text/javascript" src="morphic.js"></script>
<script type="text/javascript" src="widgets.js"></script>
<script type="text/javascript" src="blocks.js"></script>
<script type="text/javascript" src="threads.js"></script>
<script type="text/javascript" src="objects.js"></script>
<script type="text/javascript" src="gui.js"></script>
<script type="text/javascript" src="paint.js"></script>
<script type="text/javascript" src="lists.js"></script>
<script type="text/javascript" src="byob.js"></script>
<script type="text/javascript" src="xml.js"></script>
<script type="text/javascript" src="store.js"></script>
<script type="text/javascript" src="locale.js"></script>
<script type="text/javascript" src="cloud.js"></script>
<script type="text/javascript" src="sha512.js"></script>
<script type="text/javascript">
var world;
window.onload = function () {
world = new WorldMorph(document.getElementById('world'));
world.worldCanvas.focus();
new IDE_Morph().openIn(world);
loop();
};
function loop() {
requestAnimationFrame(loop);
world.doOneCycle();
}
</script>
</head>
<body style="margin: 0;">
<canvas id="world" tabindex="1" style="position: absolute;" />
</body>
</html>

Wyświetl plik

@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.threads = '2015-October-02'; modules.threads = '2015-November-16';
var ThreadManager; var ThreadManager;
var Process; var Process;
@ -128,6 +128,26 @@ function snapEquals(a, b) {
return x === y; return x === y;
} }
function invoke(block, timeout) {
// exectue the given block synchronously, i.e. without yielding.
// if a timeout (in milliseconds) is specified, abort execution
// after the timeout has been reached and throw an error.
// For debugging purposes only.
// Caution: Kids, do not try this at home!
// use ThreadManager::startProcess instead
var proc = new Process(block),
startTime = Date.now();
while (proc.isRunning()) {
if (timeout && ((Date.now() - startTime) > timeout)) {
throw (new Error("a synchronous Snap! script has timed out"));
}
proc.runStep();
}
if (block instanceof ReporterBlockMorph) {
return proc.homeContext.inputs[0];
}
}
// ThreadManager /////////////////////////////////////////////////////// // ThreadManager ///////////////////////////////////////////////////////
function ThreadManager() { function ThreadManager() {
@ -638,7 +658,8 @@ Process.prototype.evaluateInput = function (input) {
if (contains( if (contains(
[CommandSlotMorph, ReporterSlotMorph], [CommandSlotMorph, ReporterSlotMorph],
input.constructor input.constructor
) || (input instanceof CSlotMorph && !input.isStatic)) { ) || (input instanceof CSlotMorph &&
(!input.isStatic || input.isLambda))) {
// I know, this still needs yet to be done right.... // I know, this still needs yet to be done right....
ans = this.reify(ans, new List()); ans = this.reify(ans, new List());
} }