summaryrefslogtreecommitdiff
path: root/paint.js
diff options
context:
space:
mode:
Diffstat (limited to 'paint.js')
-rw-r--r--paint.js441
1 files changed, 269 insertions, 172 deletions
diff --git a/paint.js b/paint.js
index adb53f4..e6de9a2 100644
--- a/paint.js
+++ b/paint.js
@@ -1,46 +1,75 @@
/*
- paint.js
- Paint editor for Snap!
- Inspired by the Scratch paint editor.
-
- written by Kartik Chandra
-
- Latest revision: May 10 (Kartik)
-
- This file is part of Snap!.
+ paint.js
+
+ a paint editor for Snap!
+ inspired by the Scratch paint editor.
- --current changes
- Shrinkwrap
- Draw crosshairs immediately
- TRANSPARENT PAINT
- Line width viewer
+ written by Kartik Chandra
+ Copyright (C) 2013 by Kartik Chandra
+
+ This file is part of Snap!.
- --To-Do list (in rough order of priority):
- Eraser tool
-
- After release:
- --
- rgba sliders
- Import image
- Zoom/pan/Selection tools
- Pick color from canvas
+ Snap! is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+ toc
+ ---
+ the following list shows the order in which all constructors are
+ defined. Use this list to locate code in this document:
+
+ PaintEditorMorph
+ PaintColorPickerMorph
+ PaintCanvasMorph
+
+
+ credits
+ -------
+ Nathan Dinsmore contributed a fully working prototype,
+ Nathan's brilliant flood-fill tool has been more or less
+ directly imported into this paint implementation.
+
+ Jens Mönig has contributed icons and bugfixes and says he has probably
+ introduced many other bugs in that process. :-)
+
+
+ revision history
+ ----------------
+ May 10 - first full release (Kartik)
+ May 14 - bugfixes (bugfixes, Snap integration (Jens)
+
*/
/*global Point, Rectangle, DialogBoxMorph, fontHeight, AlignmentMorph,
FrameMorph, PushButtonMorph, Color, SymbolMorph, newCanvas, Morph, TextMorph,
CostumeIconMorph, IDE_Morph, Costume, SpriteMorph, nop, Image, WardrobeMorph,
TurtleIconMorph, localize, MenuMorph, InputFieldMorph, SliderMorph,
- ToggleMorph, ToggleButtonMorph, BoxMorph
+ ToggleMorph, ToggleButtonMorph, BoxMorph, modules, radians, SVG_Costume
*/
-// Global definitions
+// Global stuff ////////////////////////////////////////////////////////
+
+modules.paint = '2013-May-14';
+
+// Declarations
+
var PaintEditorMorph;
var PaintCanvasMorph;
var PaintColorPickerMorph;
// PaintEditorMorph //////////////////////////
+
// A complete paint editor
-//////////////////////////////////////////////
PaintEditorMorph.prototype = new DialogBoxMorph();
PaintEditorMorph.prototype.constructor = PaintEditorMorph;
@@ -54,7 +83,8 @@ function PaintEditorMorph() {
PaintEditorMorph.prototype.init = function() {
// additional properties:
- this.paper = null; // paint canvas
+ this.paper = null; // paint canvas
+ this.oncancel = null;
// initialize inherited properties:
PaintEditorMorph.uber.init.call(this);
@@ -70,7 +100,8 @@ PaintEditorMorph.prototype.init = function() {
PaintEditorMorph.prototype.buildContents = function () {
var myself = this;
- this.paper = new PaintCanvasMorph(function() {return myself.shift; });
+ this.paper = new PaintCanvasMorph(function() {return myself.shift; });
+ this.paper.setExtent(new Point(480, 360));
this.addBody(new AlignmentMorph('row', this.padding));
this.controls = new AlignmentMorph('column', this.padding);
@@ -113,22 +144,22 @@ PaintEditorMorph.prototype.buildContents = function () {
PaintEditorMorph.prototype.buildToolbox = function() {
var tools = {
brush:
- "Paintbrush tool (free draw)",
+ "Paintbrush tool\n(free draw)",
rectangle:
- "Stroked Rectangle (shift: square)",
+ "Stroked Rectangle\n(shift: square)",
circle:
- "Stroked Ellipse (shift: circle)",
+ "Stroked Ellipse\n(shift: circle)",
eraser:
"Eraser tool",
crosshairs:
"Set the rotation center",
line:
- "Line tool (shift: vertical/horizontal)",
+ "Line tool\n(shift: vertical/horizontal)",
rectangleSolid:
- "Filled Rectangle (shift: square)",
+ "Filled Rectangle\n(shift: square)",
circleSolid:
- "Filled Ellipse (shift: circle)",
+ "Filled Ellipse\n(shift: circle)",
paintbucket:
"Fill a region"
},
@@ -165,21 +196,18 @@ PaintEditorMorph.prototype.buildEdits = function() {
this.edits.add(this.pushButton(
"undo",
- function () {paper.undo(); },
- "Undo last action"
+ function () {paper.undo(); }
));
this.edits.add(this.pushButton(
"clear",
- function () {paper.clearCanvas(); },
- "Clear the paper"
+ function () {paper.clearCanvas(); }
));
this.edits.fixLayout();
};
-
-// Open the editor in a world with an optional image to edit
PaintEditorMorph.prototype.openIn = function(world, oldim, oldrc, callback) {
+ // Open the editor in a world with an optional image to edit
this.setCenter(world.center());
this.oldim = oldim;
this.oldrc = oldrc.copy();
@@ -194,29 +222,40 @@ PaintEditorMorph.prototype.openIn = function(world, oldim, oldrc, callback) {
this.shift = this.world().currentKey === 16;
this.propertiesControls.constrain.refresh();
};
- this.fixLayout(); // merge oldim - factor out into separate function
- this.changed();
+
+ //merge oldim:
+ if (this.oldim) {
+ this.paper.centermerge(this.oldim, this.paper.paper);
+ this.paper.rotationCenter =
+ this.oldrc.add(
+ new Point(
+ (this.paper.paper.width - this.oldim.width) / 2,
+ (this.paper.paper.height - this.oldim.height) / 2
+ )
+ );
+ this.paper.drawNew();
+ }
+
+ this.fullChanged();
};
-PaintEditorMorph.prototype.fixLayout = function() {
- if (this.paper) {
- this.paper.setExtent(new Point(480, 360));
- this.paper.fixLayout();
- if (this.oldim) {
- this.paper.centermerge(this.oldim, this.paper.paper);
- this.paper.rotationCenter =
- this.oldrc.add(
- new Point(
- (this.paper.paper.width - this.oldim.width) / 2,
- (this.paper.paper.height - this.oldim.height) / 2
- )
- );
- }
+PaintEditorMorph.prototype.fixLayout = function() {
+ var oldFlag = Morph.prototype.trackChanges;
+
+ this.changed();
+ oldFlag = Morph.prototype.trackChanges;
+ Morph.prototype.trackChanges = false;
+
+ if (this.paper) {
+ this.paper.buildContents();
this.paper.drawNew();
}
if (this.controls) {this.controls.fixLayout(); }
if (this.body) {this.body.fixLayout(); }
PaintEditorMorph.uber.fixLayout.call(this);
+
+ Morph.prototype.trackChanges = oldFlag;
+ this.changed();
};
PaintEditorMorph.prototype.refreshToolButtons = function() {
@@ -232,6 +271,11 @@ PaintEditorMorph.prototype.ok = function() {
);
this.destroy();
};
+
+PaintEditorMorph.prototype.cancel = function () {
+ if (this.oncancel) {this.oncancel(); }
+ this.destroy();
+};
PaintEditorMorph.prototype.populatePropertiesMenu = function() {
var c = this.controls,
@@ -317,7 +361,7 @@ PaintEditorMorph.prototype.populatePropertiesMenu = function() {
c.add(pc.colorpicker);
//c.add(pc.primaryColorButton);
c.add(pc.primaryColorViewer);
- c.add(new TextMorph("Pen size"));
+ c.add(new TextMorph("Brush size"));
c.add(alpen);
c.add(pc.constrain);
};
@@ -354,8 +398,8 @@ PaintEditorMorph.prototype.pushButton = function(title, action, hint) {
};
// AdvancedColorPickerMorph //////////////////
+
// A large hsl color picker
-//////////////////////////////////////////////
PaintColorPickerMorph.prototype = new Morph();
PaintColorPickerMorph.prototype.constructor = PaintColorPickerMorph;
@@ -425,11 +469,13 @@ PaintColorPickerMorph.prototype.mouseDownLeft = function(pos) {
};
PaintColorPickerMorph.prototype.mouseMove =
- PaintColorPickerMorph.prototype.mouseDownLeft;
-// PaintCanvasMorph ///////////////////////////
-// A canvas which reacts to drag events to
-// modify its image, based on a 'tool' property.
-///////////////////////////////////////////////
+ PaintColorPickerMorph.prototype.mouseDownLeft;
+
+// PaintCanvasMorph ///////////////////////////
+/*
+ A canvas which reacts to drag events to
+ modify its image, based on a 'tool' property.
+*/
PaintCanvasMorph.prototype = new Morph();
PaintCanvasMorph.prototype.constructor = PaintCanvasMorph;
@@ -440,14 +486,13 @@ function PaintCanvasMorph(shift) {
}
PaintCanvasMorph.prototype.init = function(shift) {
- this.fixLayout();
this.rotationCenter = new Point(240, 180);
this.dragRect = null;
this.previousDragPoint = null;
this.currentTool = "brush";
this.dragRect = new Rectangle();
// rectangle with origin being the starting drag position and
- // corner being the current drag position
+ // corner being the current drag position
this.mask = newCanvas(this.extent()); // Temporary canvas
this.paper = newCanvas(this.extent()); // Actual canvas
this.erasermask = newCanvas(this.extent()); // eraser memory
@@ -462,7 +507,8 @@ PaintCanvasMorph.prototype.init = function(shift) {
this.isShiftPressed = shift || function() {
var key = this.world().currentKey;
return (key === 16);
- };
+ };
+ this.buildContents();
};
PaintCanvasMorph.prototype.cacheUndo = function() {
@@ -483,7 +529,8 @@ PaintCanvasMorph.prototype.undo = function() {
PaintCanvasMorph.prototype.merge = function(a, b) {
b.getContext("2d").drawImage(a, 0, 0);
-};
+};
+
PaintCanvasMorph.prototype.centermerge = function(a, b) {
b.getContext("2d").drawImage(
a,
@@ -507,33 +554,60 @@ PaintCanvasMorph.prototype.toolChanged = function(tool) {
this.changed();
};
-PaintCanvasMorph.prototype.drawcrosshair = function() {
- var mctx = this.mask.getContext("2d"),
- pos = this.rotationCenter;
-
- mctx.strokeStyle = "rgba(0,0,0,0.6)";
- mctx.lineWidth = 5;
- mctx.beginPath();
- mctx.moveTo(pos.x, 0);
- mctx.lineTo(pos.x, this.extent().y);
- mctx.moveTo(0, pos.y);
- mctx.lineTo(this.extent().x, pos.y);
- mctx.stroke();
-
- mctx.strokeStyle = "rgba(255,255,255,0.6)";
- mctx.lineWidth = 1;
- mctx.beginPath();
- mctx.moveTo(pos.x, 0);
- mctx.lineTo(pos.x, this.extent().y);
- mctx.moveTo(0, pos.y);
- mctx.lineTo(this.extent().x, pos.y);
- mctx.stroke();
+PaintCanvasMorph.prototype.drawcrosshair = function(context) {
+ var ctx = context || this.mask.getContext("2d"),
+ rp = this.rotationCenter;
+
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = 'black';
+ ctx.clearRect(0, 0, this.mask.width, this.mask.height);
+
+ // draw crosshairs:
+ ctx.globalAlpha = 0.5;
+
+ // circle around center:
+ ctx.fillStyle = 'white';
+ ctx.beginPath();
+ ctx.arc(
+ rp.x,
+ rp.y,
+ 20,
+ radians(0),
+ radians(360),
+ false
+ );
+ ctx.closePath();
+ ctx.fill();
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.arc(
+ rp.x,
+ rp.y,
+ 10,
+ radians(0),
+ radians(360),
+ false
+ );
+ ctx.stroke();
+
+ // horizontal line:
+ ctx.beginPath();
+ ctx.moveTo(0, rp.y);
+ ctx.lineTo(this.mask.width, rp.y);
+ ctx.stroke();
+
+ // vertical line:
+ ctx.beginPath();
+ ctx.moveTo(rp.x, 0);
+ ctx.lineTo(rp.x, this.mask.height);
+ ctx.stroke();
this.drawNew();
this.changed();
};
-PaintCanvasMorph.prototype.floodfill = function(sourcepoint) {
+PaintCanvasMorph.prototype.floodfill = function(sourcepoint) {
var width = this.paper.width,
height = this.paper.height,
ctx = this.paper.getContext("2d"),
@@ -555,7 +629,7 @@ PaintCanvasMorph.prototype.floodfill = function(sourcepoint) {
p[2] === sourcecolor[2] &&
p[3] === sourcecolor[3];
};
- while (stack.length > 0) {
+ while (stack.length > 0) {
currentpoint = stack.pop();
if (checkpoint(read(currentpoint))) {
if (currentpoint % 480 > 1) {
@@ -576,7 +650,9 @@ PaintCanvasMorph.prototype.floodfill = function(sourcepoint) {
data[currentpoint * 4 + 3] = this.settings.primarycolor.a;
}
}
- ctx.putImageData(img, 0, 0);
+ ctx.putImageData(img, 0, 0);
+ this.drawNew();
+ this.changed();
};
PaintCanvasMorph.prototype.mouseDownLeft = function(pos) {
@@ -584,8 +660,13 @@ PaintCanvasMorph.prototype.mouseDownLeft = function(pos) {
this.dragRect.origin = pos.subtract(this.bounds.origin);
this.dragRect.corner = pos.subtract(this.bounds.origin);
this.previousDragPoint = this.dragRect.corner.copy();
+ if (this.currentTool === 'crosshairs') {
+ this.rotationCenter = pos.subtract(this.bounds.origin);
+ this.drawcrosshair();
+ return;
+ }
if (this.currentTool === "paintbucket") {
- this.floodfill(pos.subtract(this.bounds.origin));
+ return this.floodfill(pos.subtract(this.bounds.origin));
}
if (this.settings.primarycolor === "transparent" &&
this.currentTool !== "crosshairs") {
@@ -594,7 +675,11 @@ PaintCanvasMorph.prototype.mouseDownLeft = function(pos) {
}
};
-PaintCanvasMorph.prototype.mouseMove = function(pos) {
+PaintCanvasMorph.prototype.mouseMove = function(pos) {
+ if (this.currentTool === "paintbucket") {
+ return;
+ }
+
var relpos = pos.subtract(this.bounds.origin),
mctx = this.mask.getContext("2d"),
pctx = this.paper.getContext("2d"),
@@ -707,27 +792,9 @@ PaintCanvasMorph.prototype.mouseMove = function(pos) {
}
}
break;
- case "crosshairs":
- mctx.save();
- mctx.globalCompositeOperation = "source-over";
- mctx.strokeStyle = "rgba(0,0,0,0.6)";
- mctx.lineWidth = 5;
- mctx.beginPath();
- mctx.moveTo(p, 0);
- mctx.lineTo(p, this.extent().y);
- mctx.moveTo(0, q);
- mctx.lineTo(this.extent().x, q);
- mctx.stroke();
- mctx.strokeStyle = "rgba(255,255,255,0.6)";
- mctx.lineWidth = 1;
- mctx.beginPath();
- mctx.moveTo(p, 0);
- mctx.lineTo(p, this.extent().y);
- mctx.moveTo(0, q);
- mctx.lineTo(this.extent().x, q);
- mctx.stroke();
- this.rotationCenter = relpos.copy();
- mctx.restore();
+ case "crosshairs":
+ this.rotationCenter = relpos.copy();
+ this.drawcrosshair(mctx);
break;
case "eraser":
this.merge(this.paper, this.mask);
@@ -752,30 +819,30 @@ PaintCanvasMorph.prototype.mouseMove = function(pos) {
mctx.restore();
};
-PaintCanvasMorph.prototype.mouseClickLeft = function() {
+PaintCanvasMorph.prototype.mouseClickLeft = function() {
if (this.currentTool !== "crosshairs") {
this.merge(this.mask, this.paper);
}
this.brushBuffer = [];
};
-
-PaintCanvasMorph.prototype.fixLayout = function() {
- this.background = newCanvas(this.extent());
- this.paper = newCanvas(this.extent());
- this.mask = newCanvas(this.extent());
- this.erasermask = newCanvas(this.extent());
- var i, j, bkctx = this.background.getContext("2d");
- for (i = 0; i < this.background.width; i += 5) {
- for (j = 0; j < this.background.height; j += 5) {
- if ((i + j) / 5 % 2 === 1) {
- bkctx.fillStyle = "rgba(255, 255, 255, 1)";
- } else {
- bkctx.fillStyle = "rgba(255, 255, 255, 0.3)";
- }
- bkctx.fillRect(i, j, 5, 5);
- }
- }
-};
+
+PaintCanvasMorph.prototype.buildContents = function() {
+ this.background = newCanvas(this.extent());
+ this.paper = newCanvas(this.extent());
+ this.mask = newCanvas(this.extent());
+ this.erasermask = newCanvas(this.extent());
+ var i, j, bkctx = this.background.getContext("2d");
+ for (i = 0; i < this.background.width; i += 5) {
+ for (j = 0; j < this.background.height; j += 5) {
+ if ((i + j) / 5 % 2 === 1) {
+ bkctx.fillStyle = "rgba(255, 255, 255, 1)";
+ } else {
+ bkctx.fillStyle = "rgba(255, 255, 255, 0.3)";
+ }
+ bkctx.fillRect(i, j, 5, 5);
+ }
+ }
+};
PaintCanvasMorph.prototype.drawNew = function() {
var can = newCanvas(this.extent());
@@ -825,9 +892,16 @@ PaintCanvasMorph.prototype.contrast
// These will be incorporated into the respective files later
// They add costume editing functionality to Snap!.
////////////////////////////////////////////////////////////////////////
-CostumeIconMorph.prototype.editCostume = function () {
- var ide = this.parentThatIsA(IDE_Morph);
- this.object.edit(this.world(), ide);
+
+CostumeIconMorph.prototype.editCostume = function () {
+ if (this.object instanceof SVG_Costume) {
+ this.object.editRotationPointOnly(this.world());
+ } else {
+ this.object.edit(
+ this.world(),
+ this.parentThatIsA(IDE_Morph)
+ );
+ }
};
Costume.prototype.edit = function (aWorld, anIDE, isnew, oncancel, onsubmit) {
@@ -857,9 +931,6 @@ Costume.prototype.edit = function (aWorld, anIDE, isnew, oncancel, onsubmit) {
);
};
-
-
-
IDE_Morph.prototype.createCorralBar = function () {
// assumes the stage has already been created
var padding = 5,
@@ -869,14 +940,17 @@ IDE_Morph.prototype.createCorralBar = function () {
this.groupColor,
this.frameColor.darker(50),
this.frameColor.darker(50)
- ];
+ ];
+
if (this.corralBar) {
this.corralBar.destroy();
}
+
this.corralBar = new Morph();
this.corralBar.color = this.frameColor;
this.corralBar.setHeight(this.logo.height()); // height is fixed
this.add(this.corralBar);
+
// new sprite button
newbutton = new PushButtonMorph(
this,
@@ -899,6 +973,7 @@ IDE_Morph.prototype.createCorralBar = function () {
newbutton.setCenter(this.corralBar.center());
newbutton.setLeft(this.corralBar.left() + padding);
this.corralBar.add(newbutton);
+
paintbutton = new PushButtonMorph(
this,
"paintNewSprite",
@@ -922,21 +997,31 @@ IDE_Morph.prototype.createCorralBar = function () {
this.corralBar.left() + padding + newbutton.width() + padding
);
this.corralBar.add(paintbutton);
-};
-
-IDE_Morph.prototype.paintNewSprite = function() {
- var sprite = new SpriteMorph(this.globalVariables),
- cos = new Costume();
- sprite.name = sprite.name +
- (this.corral.frame.contents.children.length + 1);
- sprite.setCenter(this.stage.center());
- this.stage.add(sprite);
- this.sprites.add(sprite);
- this.corral.addSprite(sprite);
- this.selectSprite(sprite);
- cos.edit(this.world(), this, true, function() {sprite.remove(); });
- sprite.addCostume(cos);
-};
+};
+
+IDE_Morph.prototype.paintNewSprite = function() {
+ var sprite = new SpriteMorph(this.globalVariables),
+ cos = new Costume(),
+ myself = this;
+
+ sprite.name = sprite.name +
+ (this.corral.frame.contents.children.length + 1);
+ sprite.setCenter(this.stage.center());
+ this.stage.add(sprite);
+ this.sprites.add(sprite);
+ this.corral.addSprite(sprite);
+ this.selectSprite(sprite);
+ cos.edit(
+ this.world(),
+ this,
+ true,
+ function() {myself.removeSprite(sprite); },
+ function () {
+ sprite.addCostume(cos);
+ sprite.wearCostume(cos);
+ }
+ );
+};
WardrobeMorph.prototype.updateList = function () {
var myself = this,
@@ -948,15 +1033,12 @@ WardrobeMorph.prototype.updateList = function () {
icon,
template,
txt,
- paintbutton,
- colors = [
- new Color(50, 50, 50, 1),
- new Color(60, 60, 60, 1),
- new Color(70, 70, 70, 1)
- ];
+ paintbutton;
+
this.changed();
oldFlag = Morph.prototype.trackChanges;
Morph.prototype.trackChanges = false;
+
this.contents.destroy();
this.contents = new FrameMorph(this);
this.contents.acceptsDrops = false;
@@ -964,6 +1046,7 @@ WardrobeMorph.prototype.updateList = function () {
myself.reactToDropOf(icon);
};
this.addBack(this.contents);
+
icon = new TurtleIconMorph(this.sprite);
icon.setPosition(new Point(x, y));
myself.addContents(icon);
@@ -974,31 +1057,37 @@ WardrobeMorph.prototype.updateList = function () {
"paintNew",
new SymbolMorph("brush", 15)
);
- paintbutton.padding = 5;
+ paintbutton.padding = 0;
paintbutton.corner = 12;
- paintbutton.color = colors[0];
- paintbutton.highlightColor = colors[1];
- paintbutton.pressColor = colors[2];
+ paintbutton.color = IDE_Morph.prototype.groupColor;
+ paintbutton.highlightColor = IDE_Morph.prototype.frameColor.darker(50);
+ paintbutton.pressColor = paintbutton.highlightColor;
paintbutton.labelMinExtent = new Point(36, 18);
paintbutton.labelShadowOffset = new Point(-1, -1);
- paintbutton.labelShadowColor = colors[1];
+ paintbutton.labelShadowColor = paintbutton.highlightColor;
paintbutton.labelColor = new Color(255, 255, 255);
paintbutton.contrast = this.buttonContrast;
paintbutton.drawNew();
paintbutton.hint = "Paint a new costume";
paintbutton.setPosition(new Point(x, y));
- paintbutton.fixLayout();
-
+ paintbutton.fixLayout();
+ paintbutton.setCenter(icon.center());
+ paintbutton.setLeft(icon.right() + padding * 4);
+
+
this.addContents(paintbutton);
- y = paintbutton.bottom() + padding;
+
txt = new TextMorph(localize(
"costumes tab help" // look up long string in translator
));
txt.fontSize = 9;
txt.setColor(new Color(230, 230, 230));
+
txt.setPosition(new Point(x, y));
this.addContents(txt);
- y = txt.bottom() + padding;
+ y = txt.bottom() + padding;
+
+
this.sprite.costumes.asArray().forEach(function (costume) {
template = icon = new CostumeIconMorph(costume, template);
icon.setPosition(new Point(x, y));
@@ -1006,10 +1095,12 @@ WardrobeMorph.prototype.updateList = function () {
y = icon.bottom() + padding;
});
this.costumesVersion = this.sprite.costumes.lastChanged;
+
this.contents.setPosition(oldPos);
this.adjustScrollBars();
Morph.prototype.trackChanges = oldFlag;
this.changed();
+
this.updateSelection();
};
@@ -1029,6 +1120,14 @@ CostumeIconMorph.prototype.userMenu = function () {
var menu = new MenuMorph(this);
if (!(this.object instanceof Costume)) {return null; }
menu.addItem("edit", "editCostume");
+ if (this.world().currentKey === 16) { // shift clicked
+ menu.addItem(
+ 'edit rotation point only...',
+ 'editRotationPointOnly',
+ null,
+ new Color(100, 0, 0)
+ );
+ }
menu.addItem("rename", "renameCostume");
menu.addLine();
menu.addItem("duplicate", "duplicateCostume");
@@ -1058,7 +1157,6 @@ CostumeIconMorph.prototype.duplicateCostume = function() {
}
};
-
// I had to change this because adding a "paint new" button changed the offset
// of the costume (so the 5th child would be the 3rd costume, not the 4th as
// it was before with only the text morph child).
@@ -1072,7 +1170,6 @@ CostumeIconMorph.prototype.removeCostume = function () {
}
};
-
Costume.prototype.shrinkWrap = function () {
// adjust my contents' bounds to my visible bounding box
var bb = this.boundingBox(),