summaryrefslogtreecommitdiff
path: root/paint.js
diff options
context:
space:
mode:
Diffstat (limited to 'paint.js')
-rw-r--r--paint.js1430
1 files changed, 717 insertions, 713 deletions
diff --git a/paint.js b/paint.js
index 44585b2..15b4172 100644
--- a/paint.js
+++ b/paint.js
@@ -1,14 +1,14 @@
-/*
+/*
paint.js
-
- a paint editor for Snap!
- inspired by the Scratch paint editor.
-
+
+ a paint editor for Snap!
+ inspired by the Scratch paint editor.
+
written by Kartik Chandra
- Copyright (C) 2013 by Kartik Chandra
-
- This file is part of Snap!.
-
+ Copyright (C) 2013 by Kartik Chandra
+
+ This file is part of Snap!.
+
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
@@ -50,186 +50,187 @@
May 16 - flat design adjustments (Jens)
July 12 - pipette tool, code formatting adjustments (Jens)
September 16 - flood fill freeze fix (Kartik)
+ Jan 08 - mouse leave dragging fix (Kartik)
+
+ */
- */
-
-/*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,
+/*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, modules, radians,
- MorphicPreferences, getDocumentPositionOf
- */
-
+ MorphicPreferences, getDocumentPositionOf
+ */
+
// Global stuff ////////////////////////////////////////////////////////
-modules.paint = '2013-September-16';
+modules.paint = '2014-January-08';
// Declarations
-var PaintEditorMorph;
-var PaintCanvasMorph;
-var PaintColorPickerMorph;
-
-// PaintEditorMorph //////////////////////////
+var PaintEditorMorph;
+var PaintCanvasMorph;
+var PaintColorPickerMorph;
+
+// PaintEditorMorph //////////////////////////
+
+// A complete paint editor
-// A complete paint editor
-
-PaintEditorMorph.prototype = new DialogBoxMorph();
-PaintEditorMorph.prototype.constructor = PaintEditorMorph;
-PaintEditorMorph.uber = DialogBoxMorph.prototype;
-
-PaintEditorMorph.prototype.padding = 10;
-
-function PaintEditorMorph() {
- this.init();
-}
-
-PaintEditorMorph.prototype.init = function () {
- // additional properties:
+PaintEditorMorph.prototype = new DialogBoxMorph();
+PaintEditorMorph.prototype.constructor = PaintEditorMorph;
+PaintEditorMorph.uber = DialogBoxMorph.prototype;
+
+PaintEditorMorph.prototype.padding = 10;
+
+function PaintEditorMorph() {
+ this.init();
+}
+
+PaintEditorMorph.prototype.init = function () {
+ // additional properties:
this.paper = null; // paint canvas
- this.oncancel = null;
-
- // initialize inherited properties:
- PaintEditorMorph.uber.init.call(this);
-
- // override inherited properties:
- this.labelString = "Paint Editor";
- this.createLabel();
-
- // build contents:
- this.buildContents();
-};
-
-PaintEditorMorph.prototype.buildContents = function () {
- var myself = this;
-
+ this.oncancel = null;
+
+ // initialize inherited properties:
+ PaintEditorMorph.uber.init.call(this);
+
+ // override inherited properties:
+ this.labelString = "Paint Editor";
+ this.createLabel();
+
+ // build contents:
+ this.buildContents();
+};
+
+PaintEditorMorph.prototype.buildContents = function () {
+ var myself = this;
+
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);
- this.controls.alignment = 'left';
-
- this.edits = new AlignmentMorph('row', this.padding);
- this.buildEdits();
- this.controls.add(this.edits);
-
- this.body.color = this.color;
-
- this.body.add(this.controls);
- this.body.add(this.paper);
-
- this.toolbox = new BoxMorph();
- this.toolbox.color = SpriteMorph.prototype.paletteColor.lighter(8);
+ this.paper.setExtent(new Point(480, 360));
+
+ this.addBody(new AlignmentMorph('row', this.padding));
+ this.controls = new AlignmentMorph('column', this.padding);
+ this.controls.alignment = 'left';
+
+ this.edits = new AlignmentMorph('row', this.padding);
+ this.buildEdits();
+ this.controls.add(this.edits);
+
+ this.body.color = this.color;
+
+ this.body.add(this.controls);
+ this.body.add(this.paper);
+
+ this.toolbox = new BoxMorph();
+ this.toolbox.color = SpriteMorph.prototype.paletteColor.lighter(8);
this.toolbox.borderColor = this.toolbox.color.lighter(40);
if (MorphicPreferences.isFlat) {
this.toolbox.edge = 0;
- }
-
- this.buildToolbox();
- this.controls.add(this.toolbox);
-
- this.propertiesControls = {
- colorpicker: null,
- penSizeSlider: null,
- penSizeField: null,
- primaryColorButton: null,
- primaryColorViewer: null,
- constrain: null
- };
- this.populatePropertiesMenu();
-
- this.addButton("ok", "OK");
- this.addButton("cancel", "Cancel");
-
- this.refreshToolButtons();
- this.fixLayout();
- this.drawNew();
-};
-
-PaintEditorMorph.prototype.buildToolbox = function () {
- var tools = {
- brush:
- "Paintbrush tool\n(free draw)",
- rectangle:
- "Stroked Rectangle\n(shift: square)",
- circle:
- "Stroked Ellipse\n(shift: circle)",
- eraser:
- "Eraser tool",
- crosshairs:
- "Set the rotation center",
-
- line:
- "Line tool\n(shift: vertical/horizontal)",
- rectangleSolid:
- "Filled Rectangle\n(shift: square)",
- circleSolid:
- "Filled Ellipse\n(shift: circle)",
- paintbucket:
- "Fill a region",
+ }
+
+ this.buildToolbox();
+ this.controls.add(this.toolbox);
+
+ this.propertiesControls = {
+ colorpicker: null,
+ penSizeSlider: null,
+ penSizeField: null,
+ primaryColorButton: null,
+ primaryColorViewer: null,
+ constrain: null
+ };
+ this.populatePropertiesMenu();
+
+ this.addButton("ok", "OK");
+ this.addButton("cancel", "Cancel");
+
+ this.refreshToolButtons();
+ this.fixLayout();
+ this.drawNew();
+};
+
+PaintEditorMorph.prototype.buildToolbox = function () {
+ var tools = {
+ brush:
+ "Paintbrush tool\n(free draw)",
+ rectangle:
+ "Stroked Rectangle\n(shift: square)",
+ circle:
+ "Stroked Ellipse\n(shift: circle)",
+ eraser:
+ "Eraser tool",
+ crosshairs:
+ "Set the rotation center",
+
+ line:
+ "Line tool\n(shift: vertical/horizontal)",
+ rectangleSolid:
+ "Filled Rectangle\n(shift: square)",
+ circleSolid:
+ "Filled Ellipse\n(shift: circle)",
+ paintbucket:
+ "Fill a region",
pipette:
"Pipette tool\n(pick a color anywhere)"
- },
- myself = this,
- left = this.toolbox.left(),
- top = this.toolbox.top(),
- padding = 2,
- inset = 5,
- x = 0,
- y = 0;
-
- Object.keys(tools).forEach(function (tool) {
- var btn = myself.toolButton(tool, tools[tool]);
- btn.setPosition(new Point(
- left + x,
- top + y
- ));
- x += btn.width() + padding;
- if (tool === "crosshairs") {
- x = 0;
- y += btn.height() + padding;
- myself.paper.drawcrosshair();
- }
- myself.toolbox[tool] = btn;
- myself.toolbox.add(btn);
- });
-
- this.toolbox.bounds = this.toolbox.fullBounds().expandBy(inset * 2);
- this.toolbox.drawNew();
-};
-
-PaintEditorMorph.prototype.buildEdits = function () {
- var paper = this.paper;
-
- this.edits.add(this.pushButton(
- "undo",
- function () {paper.undo(); }
- ));
-
- this.edits.add(this.pushButton(
- "clear",
- function () {paper.clearCanvas(); }
- ));
- this.edits.fixLayout();
-};
-
-PaintEditorMorph.prototype.openIn = function (world, oldim, oldrc, callback) {
+ },
+ myself = this,
+ left = this.toolbox.left(),
+ top = this.toolbox.top(),
+ padding = 2,
+ inset = 5,
+ x = 0,
+ y = 0;
+
+ Object.keys(tools).forEach(function (tool) {
+ var btn = myself.toolButton(tool, tools[tool]);
+ btn.setPosition(new Point(
+ left + x,
+ top + y
+ ));
+ x += btn.width() + padding;
+ if (tool === "crosshairs") {
+ x = 0;
+ y += btn.height() + padding;
+ myself.paper.drawcrosshair();
+ }
+ myself.toolbox[tool] = btn;
+ myself.toolbox.add(btn);
+ });
+
+ this.toolbox.bounds = this.toolbox.fullBounds().expandBy(inset * 2);
+ this.toolbox.drawNew();
+};
+
+PaintEditorMorph.prototype.buildEdits = function () {
+ var paper = this.paper;
+
+ this.edits.add(this.pushButton(
+ "undo",
+ function () {paper.undo(); }
+ ));
+
+ this.edits.add(this.pushButton(
+ "clear",
+ function () {paper.clearCanvas(); }
+ ));
+ this.edits.fixLayout();
+};
+
+PaintEditorMorph.prototype.openIn = function (world, oldim, oldrc, callback) {
// Open the editor in a world with an optional image to edit
- this.oldim = oldim;
- this.oldrc = oldrc.copy();
- this.callback = callback || nop;
+ this.oldim = oldim;
+ this.oldrc = oldrc.copy();
+ this.callback = callback || nop;
- this.processKeyUp = function () {
- this.shift = false;
- this.propertiesControls.constrain.refresh();
- };
+ this.processKeyUp = function () {
+ this.shift = false;
+ this.propertiesControls.constrain.refresh();
+ };
- this.processKeyDown = function () {
- this.shift = this.world().currentKey === 16;
- this.propertiesControls.constrain.refresh();
- };
+ this.processKeyDown = function () {
+ this.shift = this.world().currentKey === 16;
+ this.propertiesControls.constrain.refresh();
+ };
//merge oldim:
if (this.oldim) {
@@ -244,170 +245,170 @@ PaintEditorMorph.prototype.openIn = function (world, oldim, oldrc, callback) {
this.paper.drawNew();
}
- this.key = 'paint';
- this.popUp(world);
-};
-
+ this.key = 'paint';
+ this.popUp(world);
+};
+
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);
+ 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 () {
- this.toolbox.children.forEach(function (toggle) {
- toggle.refresh();
- });
-};
-
-PaintEditorMorph.prototype.ok = function () {
- this.callback(
- this.paper.paper,
- this.paper.rotationCenter
- );
- this.destroy();
-};
+};
+
+PaintEditorMorph.prototype.refreshToolButtons = function () {
+ this.toolbox.children.forEach(function (toggle) {
+ toggle.refresh();
+ });
+};
+
+PaintEditorMorph.prototype.ok = function () {
+ this.callback(
+ this.paper.paper,
+ this.paper.rotationCenter
+ );
+ this.destroy();
+};
PaintEditorMorph.prototype.cancel = function () {
if (this.oncancel) {this.oncancel(); }
this.destroy();
};
-
-PaintEditorMorph.prototype.populatePropertiesMenu = function () {
- var c = this.controls,
- myself = this,
- pc = this.propertiesControls,
- alpen = new AlignmentMorph("row", this.padding);
-
- pc.primaryColorViewer = new Morph();
- pc.primaryColorViewer.setExtent(new Point(180, 50));
- pc.primaryColorViewer.color = new Color(0, 0, 0);
- pc.colorpicker = new PaintColorPickerMorph(
- new Point(180, 100),
- function (color) {
- var ni = newCanvas(pc.primaryColorViewer.extent()),
- ctx = ni.getContext("2d"),
- i,
- j;
- myself.paper.settings.primarycolor = color;
- if (color === "transparent") {
- for (i = 0; i < 180; i += 5) {
- for (j = 0; j < 15; j += 5) {
- ctx.fillStyle =
- ((j + i) / 5) % 2 === 0 ?
- "rgba(0, 0, 0, 0.2)" :
- "rgba(0, 0, 0, 0.5)";
- ctx.fillRect(i, j, 5, 5);
-
- }
- }
- } else {
- ctx.fillStyle = color.toString();
- ctx.fillRect(0, 0, 180, 15);
- }
- ctx.strokeStyle = "black";
- ctx.lineWidth = Math.min(myself.paper.settings.linewidth, 20);
- ctx.beginPath();
- ctx.lineCap = "round";
- ctx.moveTo(20, 30);
- ctx.lineTo(160, 30);
- ctx.stroke();
- pc.primaryColorViewer.image = ni;
- pc.primaryColorViewer.changed();
- }
- );
- pc.colorpicker.action(new Color(0, 0, 0));
-
- pc.penSizeSlider = new SliderMorph(0, 20, 5, 5);
- pc.penSizeSlider.orientation = "horizontal";
- pc.penSizeSlider.setHeight(15);
- pc.penSizeSlider.setWidth(150);
- pc.penSizeSlider.action = function (num) {
- if (pc.penSizeField) {
- pc.penSizeField.setContents(num);
- }
- myself.paper.settings.linewidth = num;
- pc.colorpicker.action(myself.paper.settings.primarycolor);
- };
- pc.penSizeField = new InputFieldMorph("5", true, null, false);
- pc.penSizeField.contents().minWidth = 20;
- pc.penSizeField.setWidth(25);
- pc.penSizeField.accept = function () {
- var val = parseFloat(pc.penSizeField.getValue());
- pc.penSizeSlider.value = val;
- pc.penSizeSlider.drawNew();
- pc.penSizeSlider.updateValue();
- this.setContents(val);
- myself.paper.settings.linewidth = val;
- this.world().keyboardReceiver = myself;
- pc.colorpicker.action(myself.paper.settings.primarycolor);
- };
- alpen.add(pc.penSizeSlider);
- alpen.add(pc.penSizeField);
- alpen.color = myself.color;
- alpen.fixLayout();
- pc.penSizeField.drawNew();
- pc.constrain = new ToggleMorph(
- "checkbox",
- this,
- function () {myself.shift = !myself.shift; },
- "Constrain proportions of shapes?\n(you can also hold shift)",
- function () {return myself.shift; }
- );
- c.add(pc.colorpicker);
- //c.add(pc.primaryColorButton);
- c.add(pc.primaryColorViewer);
- c.add(new TextMorph("Brush size"));
- c.add(alpen);
- c.add(pc.constrain);
-};
-
-PaintEditorMorph.prototype.toolButton = function (icon, hint) {
- var button, myself = this;
-
- button = new ToggleButtonMorph(
- null,
+
+PaintEditorMorph.prototype.populatePropertiesMenu = function () {
+ var c = this.controls,
+ myself = this,
+ pc = this.propertiesControls,
+ alpen = new AlignmentMorph("row", this.padding);
+
+ pc.primaryColorViewer = new Morph();
+ pc.primaryColorViewer.setExtent(new Point(180, 50));
+ pc.primaryColorViewer.color = new Color(0, 0, 0);
+ pc.colorpicker = new PaintColorPickerMorph(
+ new Point(180, 100),
+ function (color) {
+ var ni = newCanvas(pc.primaryColorViewer.extent()),
+ ctx = ni.getContext("2d"),
+ i,
+ j;
+ myself.paper.settings.primarycolor = color;
+ if (color === "transparent") {
+ for (i = 0; i < 180; i += 5) {
+ for (j = 0; j < 15; j += 5) {
+ ctx.fillStyle =
+ ((j + i) / 5) % 2 === 0 ?
+ "rgba(0, 0, 0, 0.2)" :
+ "rgba(0, 0, 0, 0.5)";
+ ctx.fillRect(i, j, 5, 5);
+
+ }
+ }
+ } else {
+ ctx.fillStyle = color.toString();
+ ctx.fillRect(0, 0, 180, 15);
+ }
+ ctx.strokeStyle = "black";
+ ctx.lineWidth = Math.min(myself.paper.settings.linewidth, 20);
+ ctx.beginPath();
+ ctx.lineCap = "round";
+ ctx.moveTo(20, 30);
+ ctx.lineTo(160, 30);
+ ctx.stroke();
+ pc.primaryColorViewer.image = ni;
+ pc.primaryColorViewer.changed();
+ }
+ );
+ pc.colorpicker.action(new Color(0, 0, 0));
+
+ pc.penSizeSlider = new SliderMorph(0, 20, 5, 5);
+ pc.penSizeSlider.orientation = "horizontal";
+ pc.penSizeSlider.setHeight(15);
+ pc.penSizeSlider.setWidth(150);
+ pc.penSizeSlider.action = function (num) {
+ if (pc.penSizeField) {
+ pc.penSizeField.setContents(num);
+ }
+ myself.paper.settings.linewidth = num;
+ pc.colorpicker.action(myself.paper.settings.primarycolor);
+ };
+ pc.penSizeField = new InputFieldMorph("5", true, null, false);
+ pc.penSizeField.contents().minWidth = 20;
+ pc.penSizeField.setWidth(25);
+ pc.penSizeField.accept = function () {
+ var val = parseFloat(pc.penSizeField.getValue());
+ pc.penSizeSlider.value = val;
+ pc.penSizeSlider.drawNew();
+ pc.penSizeSlider.updateValue();
+ this.setContents(val);
+ myself.paper.settings.linewidth = val;
+ this.world().keyboardReceiver = myself;
+ pc.colorpicker.action(myself.paper.settings.primarycolor);
+ };
+ alpen.add(pc.penSizeSlider);
+ alpen.add(pc.penSizeField);
+ alpen.color = myself.color;
+ alpen.fixLayout();
+ pc.penSizeField.drawNew();
+ pc.constrain = new ToggleMorph(
+ "checkbox",
+ this,
+ function () {myself.shift = !myself.shift; },
+ "Constrain proportions of shapes?\n(you can also hold shift)",
+ function () {return myself.shift; }
+ );
+ c.add(pc.colorpicker);
+ //c.add(pc.primaryColorButton);
+ c.add(pc.primaryColorViewer);
+ c.add(new TextMorph("Brush size"));
+ c.add(alpen);
+ c.add(pc.constrain);
+};
+
+PaintEditorMorph.prototype.toolButton = function (icon, hint) {
+ var button, myself = this;
+
+ button = new ToggleButtonMorph(
+ null,
this,
function () { // action
- myself.paper.currentTool = icon;
- myself.paper.toolChanged(icon);
+ myself.paper.currentTool = icon;
+ myself.paper.toolChanged(icon);
myself.refreshToolButtons();
if (icon === 'pipette') {
myself.getUserColor();
- }
- },
- new SymbolMorph(icon, 18),
- function () {return myself.paper.currentTool === icon; }
- );
-
- button.hint = hint;
- button.drawNew();
- button.fixLayout();
- return button;
-};
-
-PaintEditorMorph.prototype.pushButton = function (title, action, hint) {
- return new PushButtonMorph(
- this,
- action,
- title,
- null,
- hint
- );
-};
+ }
+ },
+ new SymbolMorph(icon, 18),
+ function () {return myself.paper.currentTool === icon; }
+ );
+
+ button.hint = hint;
+ button.drawNew();
+ button.fixLayout();
+ return button;
+};
+
+PaintEditorMorph.prototype.pushButton = function (title, action, hint) {
+ return new PushButtonMorph(
+ this,
+ action,
+ title,
+ null,
+ hint
+ );
+};
PaintEditorMorph.prototype.getUserColor = function () {
var myself = this,
@@ -440,167 +441,167 @@ PaintEditorMorph.prototype.getUserColor = function () {
hand.processMouseUp = mouseUpBak;
};
};
-
-// AdvancedColorPickerMorph //////////////////
-// A large hsl color picker
-
-PaintColorPickerMorph.prototype = new Morph();
-PaintColorPickerMorph.prototype.constructor = PaintColorPickerMorph;
-PaintColorPickerMorph.uber = Morph.prototype;
-
-function PaintColorPickerMorph(extent, action) {
- this.init(extent, action);
-}
-
-PaintColorPickerMorph.prototype.init = function (extent, action) {
- this.setExtent(extent || new Point(200, 100));
- this.action = action || nop;
- this.drawNew();
-};
-
-PaintColorPickerMorph.prototype.drawNew = function () {
- var x = 0,
- y = 0,
- can = newCanvas(this.extent()),
- ctx = can.getContext("2d"),
- colorselection,
- r;
- for (x = 0; x < this.width(); x += 1) {
- for (y = 0; y < this.height() - 20; y += 1) {
- ctx.fillStyle = "hsl(" +
- (360 * x / this.width()) +
- "," +
- "100%," +
- (y * 100 / (this.height() - 20)) +
- "%)";
- ctx.fillRect(x, y, 1, 1);
- }
- }
- for (x = 0; x < this.width(); x += 1) {
- r = Math.floor(255 * x / this.width());
- ctx.fillStyle = "rgb(" + r + ", " + r + ", " + r + ")";
- ctx.fillRect(x, this.height() - 20, 1, 10);
- }
- colorselection = ["black", "white", "gray"];
- for (x = 0; x < colorselection.length; x += 1) {
- ctx.fillStyle = colorselection[x];
- ctx.fillRect(
- x * this.width() / colorselection.length,
- this.height() - 10,
- this.width() / colorselection.length,
- 10
- );
- }
- for (x = this.width() * 2 / 3; x < this.width(); x += 2) {
- for (y = this.height() - 10; y < this.height(); y += 2) {
- if ((x + y) / 2 % 2 === 0) {
- ctx.fillStyle = "#DDD";
- ctx.fillRect(x, y, 2, 2);
- }
- }
- }
- this.image = can;
-};
-
-PaintColorPickerMorph.prototype.mouseDownLeft = function (pos) {
- if ((pos.subtract(this.position()).x > this.width() * 2 / 3) &&
- (pos.subtract(this.position()).y > this.height() - 10)) {
- this.action("transparent");
- } else {
- this.action(this.getPixelColor(pos));
- }
-};
-
-PaintColorPickerMorph.prototype.mouseMove =
+// AdvancedColorPickerMorph //////////////////
+
+// A large hsl color picker
+
+PaintColorPickerMorph.prototype = new Morph();
+PaintColorPickerMorph.prototype.constructor = PaintColorPickerMorph;
+PaintColorPickerMorph.uber = Morph.prototype;
+
+function PaintColorPickerMorph(extent, action) {
+ this.init(extent, action);
+}
+
+PaintColorPickerMorph.prototype.init = function (extent, action) {
+ this.setExtent(extent || new Point(200, 100));
+ this.action = action || nop;
+ this.drawNew();
+};
+
+PaintColorPickerMorph.prototype.drawNew = function () {
+ var x = 0,
+ y = 0,
+ can = newCanvas(this.extent()),
+ ctx = can.getContext("2d"),
+ colorselection,
+ r;
+ for (x = 0; x < this.width(); x += 1) {
+ for (y = 0; y < this.height() - 20; y += 1) {
+ ctx.fillStyle = "hsl(" +
+ (360 * x / this.width()) +
+ "," +
+ "100%," +
+ (y * 100 / (this.height() - 20)) +
+ "%)";
+ ctx.fillRect(x, y, 1, 1);
+ }
+ }
+ for (x = 0; x < this.width(); x += 1) {
+ r = Math.floor(255 * x / this.width());
+ ctx.fillStyle = "rgb(" + r + ", " + r + ", " + r + ")";
+ ctx.fillRect(x, this.height() - 20, 1, 10);
+ }
+ colorselection = ["black", "white", "gray"];
+ for (x = 0; x < colorselection.length; x += 1) {
+ ctx.fillStyle = colorselection[x];
+ ctx.fillRect(
+ x * this.width() / colorselection.length,
+ this.height() - 10,
+ this.width() / colorselection.length,
+ 10
+ );
+ }
+ for (x = this.width() * 2 / 3; x < this.width(); x += 2) {
+ for (y = this.height() - 10; y < this.height(); y += 2) {
+ if ((x + y) / 2 % 2 === 0) {
+ ctx.fillStyle = "#DDD";
+ ctx.fillRect(x, y, 2, 2);
+ }
+ }
+ }
+ this.image = can;
+};
+
+PaintColorPickerMorph.prototype.mouseDownLeft = function (pos) {
+ if ((pos.subtract(this.position()).x > this.width() * 2 / 3) &&
+ (pos.subtract(this.position()).y > this.height() - 10)) {
+ this.action("transparent");
+ } else {
+ this.action(this.getPixelColor(pos));
+ }
+};
+
+PaintColorPickerMorph.prototype.mouseMove =
PaintColorPickerMorph.prototype.mouseDownLeft;
-
+
// PaintCanvasMorph ///////////////////////////
/*
- A canvas which reacts to drag events to
+ A canvas which reacts to drag events to
modify its image, based on a 'tool' property.
*/
-
-PaintCanvasMorph.prototype = new Morph();
-PaintCanvasMorph.prototype.constructor = PaintCanvasMorph;
-PaintCanvasMorph.uber = Morph.prototype;
-
-function PaintCanvasMorph(shift) {
- this.init(shift);
-}
-
-PaintCanvasMorph.prototype.init = function (shift) {
- 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
+
+PaintCanvasMorph.prototype = new Morph();
+PaintCanvasMorph.prototype.constructor = PaintCanvasMorph;
+PaintCanvasMorph.uber = Morph.prototype;
+
+function PaintCanvasMorph(shift) {
+ this.init(shift);
+}
+
+PaintCanvasMorph.prototype.init = function (shift) {
+ 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
- this.mask = newCanvas(this.extent()); // Temporary canvas
- this.paper = newCanvas(this.extent()); // Actual canvas
- this.erasermask = newCanvas(this.extent()); // eraser memory
- this.background = newCanvas(this.extent()); // checkers
- this.settings = {
- "primarycolor": new Color(0, 0, 0, 255), // usually fill color
- "secondarycolor": new Color(0, 0, 0, 255), // (unused)
- "linewidth": 3 // stroke width
- };
- this.brushBuffer = [];
- this.undoBuffer = [];
- this.isShiftPressed = shift || function () {
- var key = this.world().currentKey;
- return (key === 16);
+ this.mask = newCanvas(this.extent()); // Temporary canvas
+ this.paper = newCanvas(this.extent()); // Actual canvas
+ this.erasermask = newCanvas(this.extent()); // eraser memory
+ this.background = newCanvas(this.extent()); // checkers
+ this.settings = {
+ "primarycolor": new Color(0, 0, 0, 255), // usually fill color
+ "secondarycolor": new Color(0, 0, 0, 255), // (unused)
+ "linewidth": 3 // stroke width
+ };
+ this.brushBuffer = [];
+ this.undoBuffer = [];
+ this.isShiftPressed = shift || function () {
+ var key = this.world().currentKey;
+ return (key === 16);
};
- this.buildContents();
-};
-
-PaintCanvasMorph.prototype.cacheUndo = function () {
- var cachecan = newCanvas(this.extent());
- this.merge(this.paper, cachecan);
- this.undoBuffer.push(cachecan);
-};
-
-PaintCanvasMorph.prototype.undo = function () {
- if (this.undoBuffer.length > 0) {
- this.paper = newCanvas(this.extent());
- this.mask.width = this.mask.width + 1 - 1;
- this.merge(this.undoBuffer.pop(), this.paper);
- this.drawNew();
- this.changed();
- }
-};
-
-PaintCanvasMorph.prototype.merge = function (a, b) {
- b.getContext("2d").drawImage(a, 0, 0);
+ this.buildContents();
};
-
-PaintCanvasMorph.prototype.centermerge = function (a, b) {
- b.getContext("2d").drawImage(
- a,
- (b.width - a.width) / 2,
- (b.height - a.height) / 2
- );
-};
-
+
+PaintCanvasMorph.prototype.cacheUndo = function () {
+ var cachecan = newCanvas(this.extent());
+ this.merge(this.paper, cachecan);
+ this.undoBuffer.push(cachecan);
+};
+
+PaintCanvasMorph.prototype.undo = function () {
+ if (this.undoBuffer.length > 0) {
+ this.paper = newCanvas(this.extent());
+ this.mask.width = this.mask.width + 1 - 1;
+ this.merge(this.undoBuffer.pop(), this.paper);
+ this.drawNew();
+ this.changed();
+ }
+};
+
+PaintCanvasMorph.prototype.merge = function (a, b) {
+ b.getContext("2d").drawImage(a, 0, 0);
+};
+
+PaintCanvasMorph.prototype.centermerge = function (a, b) {
+ b.getContext("2d").drawImage(
+ a,
+ (b.width - a.width) / 2,
+ (b.height - a.height) / 2
+ );
+};
+
PaintCanvasMorph.prototype.clearCanvas = function () {
- this.buildContents();
- this.drawNew();
- this.changed();
-};
-
-PaintCanvasMorph.prototype.toolChanged = function (tool) {
- this.mask = newCanvas(this.extent());
- if (tool === "crosshairs") {
- this.drawcrosshair();
- }
- this.drawNew();
- this.changed();
-};
-
-PaintCanvasMorph.prototype.drawcrosshair = function (context) {
- var ctx = context || this.mask.getContext("2d"),
- rp = this.rotationCenter;
+ this.buildContents();
+ this.drawNew();
+ this.changed();
+};
+
+PaintCanvasMorph.prototype.toolChanged = function (tool) {
+ this.mask = newCanvas(this.extent());
+ if (tool === "crosshairs") {
+ this.drawcrosshair();
+ }
+ this.drawNew();
+ this.changed();
+};
+
+PaintCanvasMorph.prototype.drawcrosshair = function (context) {
+ var ctx = context || this.mask.getContext("2d"),
+ rp = this.rotationCenter;
ctx.lineWidth = 1;
ctx.strokeStyle = 'black';
@@ -646,32 +647,32 @@ PaintCanvasMorph.prototype.drawcrosshair = function (context) {
ctx.moveTo(rp.x, 0);
ctx.lineTo(rp.x, this.mask.height);
ctx.stroke();
-
- this.drawNew();
- this.changed();
-};
-
+
+ this.drawNew();
+ this.changed();
+};
+
PaintCanvasMorph.prototype.floodfill = function (sourcepoint) {
- var width = this.paper.width,
- height = this.paper.height,
- ctx = this.paper.getContext("2d"),
- img = ctx.getImageData(0, 0, width, height),
- data = img.data,
- stack = [Math.round(sourcepoint.y) * width + sourcepoint.x],
- currentpoint,
- read,
- sourcecolor,
- checkpoint;
- read = function (p) {
- var d = p * 4;
- return [data[d], data[d + 1], data[d + 2], data[d + 3]];
- };
- sourcecolor = read(stack[0]);
- checkpoint = function (p) {
- return p[0] === sourcecolor[0] &&
- p[1] === sourcecolor[1] &&
- p[2] === sourcecolor[2] &&
- p[3] === sourcecolor[3];
+ var width = this.paper.width,
+ height = this.paper.height,
+ ctx = this.paper.getContext("2d"),
+ img = ctx.getImageData(0, 0, width, height),
+ data = img.data,
+ stack = [Math.round(sourcepoint.y) * width + sourcepoint.x],
+ currentpoint,
+ read,
+ sourcecolor,
+ checkpoint;
+ read = function (p) {
+ var d = p * 4;
+ return [data[d], data[d + 1], data[d + 2], data[d + 3]];
+ };
+ sourcecolor = read(stack[0]);
+ checkpoint = function (p) {
+ return p[0] === sourcecolor[0] &&
+ p[1] === sourcecolor[1] &&
+ p[2] === sourcecolor[2] &&
+ p[3] === sourcecolor[3];
};
// if already filled, abort
@@ -690,201 +691,204 @@ PaintCanvasMorph.prototype.floodfill = function (sourcepoint) {
}
while (stack.length > 0) {
- currentpoint = stack.pop();
- if (checkpoint(read(currentpoint))) {
- if (currentpoint % 480 > 1) {
- stack.push(currentpoint + 1);
- stack.push(currentpoint - 1);
- }
- if (currentpoint > 0 && currentpoint < 360 * 480) {
- stack.push(currentpoint + width);
- stack.push(currentpoint - width);
- }
- }
- if (this.settings.primarycolor === "transparent") {
- data[currentpoint * 4 + 3] = 0;
- } else {
- data[currentpoint * 4] = this.settings.primarycolor.r;
- data[currentpoint * 4 + 1] = this.settings.primarycolor.g;
- data[currentpoint * 4 + 2] = this.settings.primarycolor.b;
- data[currentpoint * 4 + 3] = this.settings.primarycolor.a;
- }
- }
+ currentpoint = stack.pop();
+ if (checkpoint(read(currentpoint))) {
+ if (currentpoint % 480 > 1) {
+ stack.push(currentpoint + 1);
+ stack.push(currentpoint - 1);
+ }
+ if (currentpoint > 0 && currentpoint < 360 * 480) {
+ stack.push(currentpoint + width);
+ stack.push(currentpoint - width);
+ }
+ }
+ if (this.settings.primarycolor === "transparent") {
+ data[currentpoint * 4 + 3] = 0;
+ } else {
+ data[currentpoint * 4] = this.settings.primarycolor.r;
+ data[currentpoint * 4 + 1] = this.settings.primarycolor.g;
+ data[currentpoint * 4 + 2] = this.settings.primarycolor.b;
+ data[currentpoint * 4 + 3] = this.settings.primarycolor.a;
+ }
+ }
ctx.putImageData(img, 0, 0);
this.drawNew();
this.changed();
-};
-
-PaintCanvasMorph.prototype.mouseDownLeft = function (pos) {
- this.cacheUndo();
- this.dragRect.origin = pos.subtract(this.bounds.origin);
- this.dragRect.corner = pos.subtract(this.bounds.origin);
- this.previousDragPoint = this.dragRect.corner.copy();
+};
+
+PaintCanvasMorph.prototype.mouseDownLeft = function (pos) {
+ this.cacheUndo();
+ 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") {
- return this.floodfill(pos.subtract(this.bounds.origin));
- }
- if (this.settings.primarycolor === "transparent" &&
- this.currentTool !== "crosshairs") {
- this.erasermask = newCanvas(this.extent());
- this.merge(this.paper, this.erasermask);
- }
-};
-
+ if (this.currentTool === "paintbucket") {
+ return this.floodfill(pos.subtract(this.bounds.origin));
+ }
+ if (this.settings.primarycolor === "transparent" &&
+ this.currentTool !== "crosshairs") {
+ this.erasermask = newCanvas(this.extent());
+ this.merge(this.paper, this.erasermask);
+ }
+};
+
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"),
- x = this.dragRect.origin.x, // original drag X
- y = this.dragRect.origin.y, // original drag y
- p = relpos.x, // current drag x
- q = relpos.y, // current drag y
- w = (p - x) / 2, // half the rect width
- h = (q - y) / 2, // half the rect height
- i; // iterator number
- mctx.save();
- function newW() {
- return Math.max(Math.abs(w), Math.abs(h)) * (w / Math.abs(w));
- }
- function newH() {
- return Math.max(Math.abs(w), Math.abs(h)) * (h / Math.abs(h));
- }
- this.brushBuffer.push([p, q]);
- mctx.lineWidth = this.settings.linewidth;
- mctx.clearRect(0, 0, this.bounds.width(), this.bounds.height()); // mask
-
- this.dragRect.corner = relpos.subtract(this.dragRect.origin); // reset crn
-
- if (this.settings.primarycolor === "transparent" &&
- this.currentTool !== "crosshairs") {
- this.merge(this.erasermask, this.mask);
- pctx.clearRect(0, 0, this.bounds.width(), this.bounds.height());
- mctx.globalCompositeOperation = "destination-out";
- } else {
- mctx.fillStyle = this.settings.primarycolor.toString();
- mctx.strokeStyle = this.settings.primarycolor.toString();
- }
- switch (this.currentTool) {
- case "rectangle":
- if (this.isShiftPressed()) {
- mctx.strokeRect(x, y, newW() * 2, newH() * 2);
- } else {
- mctx.strokeRect(x, y, w * 2, h * 2);
- }
- break;
- case "rectangleSolid":
- if (this.isShiftPressed()) {
- mctx.fillRect(x, y, newW() * 2, newH() * 2);
- } else {
- mctx.fillRect(x, y, w * 2, h * 2);
- }
- break;
- case "brush":
- mctx.lineCap = "round";
- mctx.lineJoin = "round";
- mctx.beginPath();
- mctx.moveTo(this.brushBuffer[0][0], this.brushBuffer[0][1]);
- for (i = 0; i < this.brushBuffer.length; i += 1) {
- mctx.lineTo(this.brushBuffer[i][0], this.brushBuffer[i][1]);
- }
- mctx.stroke();
- break;
- case "line":
- mctx.beginPath();
- mctx.moveTo(x, y);
- if (this.isShiftPressed()) {
- if (Math.abs(h) > Math.abs(w)) {
- mctx.lineTo(x, q);
- } else {
- mctx.lineTo(p, y);
- }
- } else {
- mctx.lineTo(p, q);
- }
- mctx.stroke();
- break;
- case "circle":
- case "circleSolid":
- mctx.beginPath();
- if (this.isShiftPressed()) {
- mctx.arc(
- x,
- y,
- new Point(x, y).distanceTo(new Point(p, q)),
- 0,
- Math.PI * 2,
- false
- );
- } else {
- for (i = 0; i < 480; i += 1) {
- mctx.lineTo(
- i,
- (2 * h) * Math.sqrt(2 - Math.pow(
- (i - x) / (2 * w),
- 2
- )) + y
- );
- }
- for (i = 480; i > 0; i -= 1) {
- mctx.lineTo(
- i,
- -1 * (2 * h) * Math.sqrt(2 - Math.pow(
- (i - x) / (2 * w),
- 2
- )) + y
- );
- }
- }
- mctx.closePath();
- if (this.currentTool === "circleSolid") {
- mctx.fill();
- } else {
- if (this.currentTool === "circle") {
- mctx.stroke();
- }
- }
- break;
+
+ var relpos = pos.subtract(this.bounds.origin),
+ mctx = this.mask.getContext("2d"),
+ pctx = this.paper.getContext("2d"),
+ x = this.dragRect.origin.x, // original drag X
+ y = this.dragRect.origin.y, // original drag y
+ p = relpos.x, // current drag x
+ q = relpos.y, // current drag y
+ w = (p - x) / 2, // half the rect width
+ h = (q - y) / 2, // half the rect height
+ i; // iterator number
+ mctx.save();
+ function newW() {
+ return Math.max(Math.abs(w), Math.abs(h)) * (w / Math.abs(w));
+ }
+ function newH() {
+ return Math.max(Math.abs(w), Math.abs(h)) * (h / Math.abs(h));
+ }
+ this.brushBuffer.push([p, q]);
+ mctx.lineWidth = this.settings.linewidth;
+ mctx.clearRect(0, 0, this.bounds.width(), this.bounds.height()); // mask
+
+ this.dragRect.corner = relpos.subtract(this.dragRect.origin); // reset crn
+
+ if (this.settings.primarycolor === "transparent" &&
+ this.currentTool !== "crosshairs") {
+ this.merge(this.erasermask, this.mask);
+ pctx.clearRect(0, 0, this.bounds.width(), this.bounds.height());
+ mctx.globalCompositeOperation = "destination-out";
+ } else {
+ mctx.fillStyle = this.settings.primarycolor.toString();
+ mctx.strokeStyle = this.settings.primarycolor.toString();
+ }
+ switch (this.currentTool) {
+ case "rectangle":
+ if (this.isShiftPressed()) {
+ mctx.strokeRect(x, y, newW() * 2, newH() * 2);
+ } else {
+ mctx.strokeRect(x, y, w * 2, h * 2);
+ }
+ break;
+ case "rectangleSolid":
+ if (this.isShiftPressed()) {
+ mctx.fillRect(x, y, newW() * 2, newH() * 2);
+ } else {
+ mctx.fillRect(x, y, w * 2, h * 2);
+ }
+ break;
+ case "brush":
+ mctx.lineCap = "round";
+ mctx.lineJoin = "round";
+ mctx.beginPath();
+ mctx.moveTo(this.brushBuffer[0][0], this.brushBuffer[0][1]);
+ for (i = 0; i < this.brushBuffer.length; i += 1) {
+ mctx.lineTo(this.brushBuffer[i][0], this.brushBuffer[i][1]);
+ }
+ mctx.stroke();
+ break;
+ case "line":
+ mctx.beginPath();
+ mctx.moveTo(x, y);
+ if (this.isShiftPressed()) {
+ if (Math.abs(h) > Math.abs(w)) {
+ mctx.lineTo(x, q);
+ } else {
+ mctx.lineTo(p, y);
+ }
+ } else {
+ mctx.lineTo(p, q);
+ }
+ mctx.stroke();
+ break;
+ case "circle":
+ case "circleSolid":
+ mctx.beginPath();
+ if (this.isShiftPressed()) {
+ mctx.arc(
+ x,
+ y,
+ new Point(x, y).distanceTo(new Point(p, q)),
+ 0,
+ Math.PI * 2,
+ false
+ );
+ } else {
+ for (i = 0; i < 480; i += 1) {
+ mctx.lineTo(
+ i,
+ (2 * h) * Math.sqrt(2 - Math.pow(
+ (i - x) / (2 * w),
+ 2
+ )) + y
+ );
+ }
+ for (i = 480; i > 0; i -= 1) {
+ mctx.lineTo(
+ i,
+ -1 * (2 * h) * Math.sqrt(2 - Math.pow(
+ (i - x) / (2 * w),
+ 2
+ )) + y
+ );
+ }
+ }
+ mctx.closePath();
+ if (this.currentTool === "circleSolid") {
+ mctx.fill();
+ } else {
+ if (this.currentTool === "circle") {
+ mctx.stroke();
+ }
+ }
+ break;
case "crosshairs":
this.rotationCenter = relpos.copy();
this.drawcrosshair(mctx);
- break;
- case "eraser":
- this.merge(this.paper, this.mask);
- mctx.save();
- mctx.globalCompositeOperation = "destination-out";
- mctx.beginPath();
- mctx.moveTo(this.brushBuffer[0][0], this.brushBuffer[0][1]);
- for (i = 0; i < this.brushBuffer.length; i += 1) {
- mctx.lineTo(this.brushBuffer[i][0], this.brushBuffer[i][1]);
- }
- mctx.stroke();
- mctx.restore();
- this.paper = newCanvas(this.extent());
- this.merge(this.mask, this.paper);
- break;
- default:
- nop();
- }
- this.previousDragPoint = relpos;
- this.drawNew();
- this.changed();
- mctx.restore();
-};
-
+ break;
+ case "eraser":
+ this.merge(this.paper, this.mask);
+ mctx.save();
+ mctx.globalCompositeOperation = "destination-out";
+ mctx.beginPath();
+ mctx.moveTo(this.brushBuffer[0][0], this.brushBuffer[0][1]);
+ for (i = 0; i < this.brushBuffer.length; i += 1) {
+ mctx.lineTo(this.brushBuffer[i][0], this.brushBuffer[i][1]);
+ }
+ mctx.stroke();
+ mctx.restore();
+ this.paper = newCanvas(this.extent());
+ this.merge(this.mask, this.paper);
+ break;
+ default:
+ nop();
+ }
+ this.previousDragPoint = relpos;
+ this.drawNew();
+ this.changed();
+ mctx.restore();
+};
+
PaintCanvasMorph.prototype.mouseClickLeft = function () {
- if (this.currentTool !== "crosshairs") {
- this.merge(this.mask, this.paper);
- }
- this.brushBuffer = [];
-};
+ if (this.currentTool !== "crosshairs") {
+ this.merge(this.mask, this.paper);
+ }
+ this.brushBuffer = [];
+};
+
+PaintCanvasMorph.prototype.mouseLeaveDragging
+ = PaintCanvasMorph.prototype.mouseClickLeft;
PaintCanvasMorph.prototype.buildContents = function () {
this.background = newCanvas(this.extent());
@@ -903,47 +907,47 @@ PaintCanvasMorph.prototype.buildContents = function () {
}
}
};
-
-PaintCanvasMorph.prototype.drawNew = function () {
- var can = newCanvas(this.extent());
- this.merge(this.background, can);
- this.merge(this.paper, can);
- this.merge(this.mask, can);
- this.image = can;
- this.drawFrame();
-};
-
-PaintCanvasMorph.prototype.drawFrame = function () {
- var context, borderColor;
-
- context = this.image.getContext('2d');
- if (this.parent) {
- this.color = this.parent.color.lighter(this.contrast * 0.75);
- borderColor = this.parent.color;
- } else {
- borderColor = new Color(120, 120, 120);
- }
- context.fillStyle = this.color.toString();
-
- // cache my border colors
- this.cachedClr = borderColor.toString();
- this.cachedClrBright = borderColor.lighter(this.contrast)
- .toString();
- this.cachedClrDark = borderColor.darker(this.contrast).toString();
- this.drawRectBorder(context);
-};
-
-PaintCanvasMorph.prototype.drawRectBorder
- = InputFieldMorph.prototype.drawRectBorder;
-
-PaintCanvasMorph.prototype.edge
- = InputFieldMorph.prototype.edge;
-
-PaintCanvasMorph.prototype.fontSize
- = InputFieldMorph.prototype.fontSize;
-
-PaintCanvasMorph.prototype.typeInPadding
- = InputFieldMorph.prototype.typeInPadding;
-
-PaintCanvasMorph.prototype.contrast
- = InputFieldMorph.prototype.contrast;
+
+PaintCanvasMorph.prototype.drawNew = function () {
+ var can = newCanvas(this.extent());
+ this.merge(this.background, can);
+ this.merge(this.paper, can);
+ this.merge(this.mask, can);
+ this.image = can;
+ this.drawFrame();
+};
+
+PaintCanvasMorph.prototype.drawFrame = function () {
+ var context, borderColor;
+
+ context = this.image.getContext('2d');
+ if (this.parent) {
+ this.color = this.parent.color.lighter(this.contrast * 0.75);
+ borderColor = this.parent.color;
+ } else {
+ borderColor = new Color(120, 120, 120);
+ }
+ context.fillStyle = this.color.toString();
+
+ // cache my border colors
+ this.cachedClr = borderColor.toString();
+ this.cachedClrBright = borderColor.lighter(this.contrast)
+ .toString();
+ this.cachedClrDark = borderColor.darker(this.contrast).toString();
+ this.drawRectBorder(context);
+};
+
+PaintCanvasMorph.prototype.drawRectBorder
+ = InputFieldMorph.prototype.drawRectBorder;
+
+PaintCanvasMorph.prototype.edge
+ = InputFieldMorph.prototype.edge;
+
+PaintCanvasMorph.prototype.fontSize
+ = InputFieldMorph.prototype.fontSize;
+
+PaintCanvasMorph.prototype.typeInPadding
+ = InputFieldMorph.prototype.typeInPadding;
+
+PaintCanvasMorph.prototype.contrast
+ = InputFieldMorph.prototype.contrast;