summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjmoenig <jens@moenig.org>2013-04-21 20:40:24 +0200
committerjmoenig <jens@moenig.org>2013-04-21 20:40:24 +0200
commitf21416527fd7f53b7c7eff6027a5511c4bccfaa7 (patch)
treec31f3009af9d6798c0c943ef1c601a9e1f10c484
parent977da0314c01ce8d3cda160cb913e12d4a980f36 (diff)
downloadsnap-f21416527fd7f53b7c7eff6027a5511c4bccfaa7.tar.gz
snap-f21416527fd7f53b7c7eff6027a5511c4bccfaa7.zip
Double click support for project dialogs
double clicking a project in the project dialog now performs the specified action for the dialog (open / save) on it.
-rw-r--r--gui.js13
-rwxr-xr-xhistory.txt2
-rw-r--r--morphic.js125
-rwxr-xr-xmorphic.txt1507
4 files changed, 883 insertions, 764 deletions
diff --git a/gui.js b/gui.js
index f2ac1ac..604ab06 100644
--- a/gui.js
+++ b/gui.js
@@ -68,7 +68,7 @@ sb, CommentMorph, CommandBlockMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.gui = '2013-April-19';
+modules.gui = '2013-April-21';
// Declarations
@@ -3738,7 +3738,9 @@ ProjectDialogMorph.prototype.setSource = function (source) {
this.projectList.length > 0 ?
function (element) {
return element.name;
- } : null
+ } : null,
+ null,
+ function () {myself.ok(); }
);
this.fixListFieldItemColors();
@@ -3754,6 +3756,7 @@ ProjectDialogMorph.prototype.setSource = function (source) {
this.listField.action = function (item) {
var src, xml;
+ if (item === undefined) {return; }
if (myself.nameField) {
myself.nameField.setContents(item.name || '');
}
@@ -3775,6 +3778,7 @@ ProjectDialogMorph.prototype.setSource = function (source) {
};
} else { // 'examples', 'cloud' is initialized elsewhere
this.listField.action = function (item) {
+ if (item === undefined) {return; }
if (myself.nameField) {
myself.nameField.setContents(item.name || '');
}
@@ -3852,7 +3856,9 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) {
'bold',
function (proj) {return proj.Public === 'true'; }
]
- ]
+ ],
+ null,
+ function () {myself.ok(); }
);
this.fixListFieldItemColors();
@@ -3865,6 +3871,7 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) {
this.listField.drawRectBorder = InputFieldMorph.prototype.drawRectBorder;
this.listField.action = function (item) {
+ if (item === undefined) {return; }
if (myself.nameField) {
myself.nameField.setContents(item.ProjectName || '');
}
diff --git a/history.txt b/history.txt
index f8cb536..8c39a80 100755
--- a/history.txt
+++ b/history.txt
@@ -1647,3 +1647,5 @@ ______
130421
------
* using the percent character in variable names is now safe (fixes Github issue #65)
+* Morphic: added Doubleclick support, example: inspectors
+* GUI: Double clicking a project in the project dialog performs the dialog's action on it (open / save)
diff --git a/morphic.js b/morphic.js
index 0faa43c..590337a 100644
--- a/morphic.js
+++ b/morphic.js
@@ -147,6 +147,7 @@
III. yet to implement
---------------------
- keyboard support for scroll frames and lists
+ - virtual keyboard support for Android and IE
IV. open issues
@@ -416,7 +417,7 @@
* add(submorph) - attaches submorph ontop
* addBack(submorph) - attaches submorph underneath
- * fullCopy() - duplication
+ * fullCopy() - duplication
* destroy() - deletion
@@ -450,6 +451,7 @@
mouseDownRight
mouseClickLeft
mouseClickRight
+ mouseDoubleClick
mouseEnter
mouseLeave
mouseEnterDragging
@@ -1033,7 +1035,7 @@
/*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio,
FileList, getBlurredShadowSupport*/
-var morphicVersion = '2013-April-19';
+var morphicVersion = '2013-April-21';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@@ -6161,7 +6163,7 @@ InspectorMorph.prototype.setTarget = function (target) {
};
InspectorMorph.prototype.buildPanes = function () {
- var attribs = [], property, myself = this, ctrl, ev;
+ var attribs = [], property, myself = this, ctrl, ev, doubleClickAction;
// remove existing panes
this.children.forEach(function (m) {
@@ -6194,6 +6196,20 @@ InspectorMorph.prototype.buildPanes = function () {
return typeof myself.target[prop] === 'function';
});
} // otherwise show all properties
+
+ doubleClickAction = function () {
+ var world, inspector;
+ if (!isObject(myself.currentProperty)) {return; }
+ world = myself.world();
+ inspector = new InspectorMorph(
+ myself.currentProperty
+ );
+ inspector.setPosition(world.hand.position());
+ inspector.keepWithin(world);
+ world.add(inspector);
+ inspector.changed();
+ };
+
this.list = new ListMorph(
this.target instanceof Array ? attribs : attribs.sort(),
null, // label getter
@@ -6209,10 +6225,13 @@ InspectorMorph.prototype.buildPanes = function () {
}
]
]
- : null
+ : null,
+ doubleClickAction
);
+
this.list.action = function (selected) {
var val, txt, cnts;
+ if (selected === undefined) {return; }
val = myself.target[selected];
myself.currentProperty = val;
if (val === null) {
@@ -6228,6 +6247,7 @@ InspectorMorph.prototype.buildPanes = function () {
cnts.setReceiver(myself.target);
myself.detail.setContents(cnts);
};
+
this.list.hBar.alpha = 0.6;
this.list.vBar.alpha = 0.6;
this.list.contents.step = null;
@@ -6609,7 +6629,8 @@ MenuMorph.prototype.addItem = function (
hint,
color,
bold, // bool
- italic // bool
+ italic, // bool
+ doubleClickAction // optional, when used as list contents
) {
/*
labelString is normally a single-line string. But it can also be one
@@ -6625,7 +6646,8 @@ MenuMorph.prototype.addItem = function (
hint,
color,
bold || false,
- italic || false]);
+ italic || false,
+ doubleClickAction]);
};
MenuMorph.prototype.addLine = function (width) {
@@ -6713,7 +6735,8 @@ MenuMorph.prototype.drawNew = function () {
tuple[2], // bubble help hint
tuple[3], // color
tuple[4], // bold
- tuple[5] // italic
+ tuple[5], // italic
+ tuple[6] // doubleclick action
);
}
if (isLine) {
@@ -7910,7 +7933,8 @@ function TriggerMorph(
hint,
labelColor,
labelBold,
- labelItalic
+ labelItalic,
+ doubleClickAction
) {
this.init(
target,
@@ -7922,7 +7946,8 @@ function TriggerMorph(
hint,
labelColor,
labelBold,
- labelItalic
+ labelItalic,
+ doubleClickAction
);
}
@@ -7936,11 +7961,13 @@ TriggerMorph.prototype.init = function (
hint,
labelColor,
labelBold,
- labelItalic
+ labelItalic,
+ doubleClickAction
) {
// additional properties:
this.target = target || null;
this.action = action || null;
+ this.doubleClickAction = doubleClickAction || null;
this.environment = environment || null;
this.labelString = labelString || null;
this.label = null;
@@ -8067,6 +8094,29 @@ TriggerMorph.prototype.trigger = function () {
}
};
+TriggerMorph.prototype.triggerDoubleClick = function () {
+ // same as trigger() but use doubleClickAction instead of action property
+ // note that specifying a doubleClickAction is optional
+ if (!this.doubleClickAction) {return; }
+ if (typeof this.target === 'function') {
+ if (typeof this.doubleClickAction === 'function') {
+ this.target.call(
+ this.environment,
+ this.doubleClickAction.call(),
+ this
+ );
+ } else {
+ this.target.call(this.environment, this.doubleClickAction, this);
+ }
+ } else {
+ if (typeof this.doubleClickAction === 'function') {
+ this.doubleClickAction.call(this.target);
+ } else { // assume it's a String
+ this.target[this.doubleClickAction]();
+ }
+ }
+};
+
// TriggerMorph events:
TriggerMorph.prototype.mouseEnter = function () {
@@ -8096,6 +8146,10 @@ TriggerMorph.prototype.mouseClickLeft = function () {
this.trigger();
};
+TriggerMorph.prototype.mouseDoubleClick = function () {
+ this.triggerDoubleClick();
+};
+
TriggerMorph.prototype.rootForGrab = function () {
return null;
};
@@ -8147,7 +8201,8 @@ function MenuItemMorph(
hint,
color,
bold,
- italic
+ italic,
+ doubleClickAction // optional when used as list morph item
) {
this.init(
target,
@@ -8159,7 +8214,8 @@ function MenuItemMorph(
hint,
color,
bold,
- italic
+ italic,
+ doubleClickAction
);
}
@@ -8859,7 +8915,7 @@ ListMorph.prototype = new ScrollFrameMorph();
ListMorph.prototype.constructor = ListMorph;
ListMorph.uber = ScrollFrameMorph.prototype;
-function ListMorph(elements, labelGetter, format) {
+function ListMorph(elements, labelGetter, format, doubleClickAction) {
/*
passing a format is optional. If the format parameter is specified
it has to be of the following pattern:
@@ -8891,11 +8947,17 @@ function ListMorph(elements, labelGetter, format) {
}
return element.toString();
},
- format || []
+ format || [],
+ doubleClickAction // optional callback
);
}
-ListMorph.prototype.init = function (elements, labelGetter, format) {
+ListMorph.prototype.init = function (
+ elements,
+ labelGetter,
+ format,
+ doubleClickAction
+) {
ListMorph.uber.init.call(this);
this.contents.acceptsDrops = false;
@@ -8909,6 +8971,7 @@ ListMorph.prototype.init = function (elements, labelGetter, format) {
this.selected = null; // actual element currently selected
this.active = null; // menu item representing the selected element
this.action = null;
+ this.doubleClickAction = doubleClickAction || null;
this.acceptsDrops = false;
this.buildListContents();
};
@@ -8948,7 +9011,8 @@ ListMorph.prototype.buildListContents = function () {
null, // hint
color,
bold,
- italic
+ italic,
+ myself.doubleClickAction
);
});
this.listContents.setPosition(this.contents.position());
@@ -9340,6 +9404,7 @@ HandMorph.prototype.drop = function () {
mouseDownRight
mouseClickLeft
mouseClickRight
+ mouseDoubleClick
mouseEnter
mouseLeave
mouseEnterDragging
@@ -9472,6 +9537,23 @@ HandMorph.prototype.processMouseUp = function () {
this.mouseButton = null;
};
+HandMorph.prototype.processDoubleClick = function () {
+ var morph = this.morphAtPointer();
+
+ this.destroyTemporaries();
+ if (this.children.length !== 0) {
+ this.drop();
+ } else {
+ while (morph && !morph.mouseDoubleClick) {
+ morph = morph.parent;
+ }
+ if (morph) {
+ morph.mouseDoubleClick(this.bounds.origin);
+ }
+ }
+ this.mouseButton = null;
+};
+
HandMorph.prototype.processMouseMove = function (event) {
var pos,
posInDocument = getDocumentPositionOf(this.world.worldCanvas),
@@ -9525,7 +9607,7 @@ HandMorph.prototype.processMouseMove = function (event) {
/*
original, more cautious code for grabbing Morphs,
- retained in case of needing to fall back:
+ retained in case of needing to fall back:
if (morph === this.morphToGrab) {
if (morph.isDraggable) {
@@ -10040,6 +10122,15 @@ WorldMorph.prototype.initEventListeners = function () {
);
canvas.addEventListener(
+ "dblclick",
+ function (event) {
+ event.preventDefault();
+ myself.hand.processDoubleClick(event);
+ },
+ false
+ );
+
+ canvas.addEventListener(
"touchend",
function (event) {
myself.hand.processTouchEnd(event);
diff --git a/morphic.txt b/morphic.txt
index afa6096..042b7e5 100755
--- a/morphic.txt
+++ b/morphic.txt
@@ -9,7 +9,7 @@
Copyright (C) 2012 by Jens Mönig
- this documentation last changed: November 07, 2012
+ this documentation last changed: April 07, 2013
This file is part of Snap!.
@@ -27,986 +27,1005 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
- documentation contents
- ----------------------
- I. inheritance hierarchy
- II. object definition toc
- III. yet to implement
- IV. open issues
- V. browser compatibility
- VI. the big picture
- VII. programming guide
- (1) setting up a web page
- (a) single world
- (b) multiple worlds
- (c) an application
- (2) manipulating morphs
- (3) events
- (a) mouse events
- (b) context menu
- (c) dragging
- (d) dropping
- (e) keyboard events
- (f) resize event
- (g) combined mouse-keyboard events
- (h) text editing events
- (4) stepping
- (5) creating new kinds of morphs
- (6) development and user modes
- (7) turtle graphics
- (8) damage list housekeeping
- (9) minifying morphic.js
- VIII. acknowledgements
- IX. contributors
+ documentation contents
+ ----------------------
+ I. inheritance hierarchy
+ II. object definition toc
+ III. yet to implement
+ IV. open issues
+ V. browser compatibility
+ VI. the big picture
+ VII. programming guide
+ (1) setting up a web page
+ (a) single world
+ (b) multiple worlds
+ (c) an application
+ (2) manipulating morphs
+ (3) events
+ (a) mouse events
+ (b) context menu
+ (c) dragging
+ (d) dropping
+ (e) keyboard events
+ (f) resize event
+ (g) combined mouse-keyboard events
+ (h) text editing events
+ (4) stepping
+ (5) creating new kinds of morphs
+ (6) development and user modes
+ (7) turtle graphics
+ (8) damage list housekeeping
+ (9) minifying morphic.js
+ VIII. acknowledgements
+ IX. contributors
- I. hierarchy
- -------------
- the following tree lists all constructors hierarchically,
- indentation indicating inheritance. Refer to this list to get a
- contextual overview:
+ I. hierarchy
+ -------------
+ the following tree lists all constructors hierarchically,
+ indentation indicating inheritance. Refer to this list to get a
+ contextual overview:
- Color
- Node
- Morph
- BlinkerMorph
- CursorMorph
- BouncerMorph*
- BoxMorph
- InspectorMorph
- MenuMorph
- MouseSensorMorph*
- SpeechBubbleMorph
- CircleBoxMorph
- SliderButtonMorph
- SliderMorph
- ColorPaletteMorph
- GrayPaletteMorph
- ColorPickerMorph
- FrameMorph
- ScrollFrameMorph
- ListMorph
- StringFieldMorph
- WorldMorph
- HandleMorph
- HandMorph
- PenMorph
- ShadowMorph
- StringMorph
- TextMorph
- TriggerMorph
- MenuItemMorph
- Point
- Rectangle
+ Color
+ Node
+ Morph
+ BlinkerMorph
+ CursorMorph
+ BouncerMorph*
+ BoxMorph
+ InspectorMorph
+ MenuMorph
+ MouseSensorMorph*
+ SpeechBubbleMorph
+ CircleBoxMorph
+ SliderButtonMorph
+ SliderMorph
+ ColorPaletteMorph
+ GrayPaletteMorph
+ ColorPickerMorph
+ FrameMorph
+ ScrollFrameMorph
+ ListMorph
+ StringFieldMorph
+ WorldMorph
+ HandleMorph
+ HandMorph
+ PenMorph
+ ShadowMorph
+ StringMorph
+ TextMorph
+ TriggerMorph
+ MenuItemMorph
+ Point
+ Rectangle
- II. toc
- -------
- the following list shows the order in which all constructors are
- defined. Use this list to locate code in this document:
+ II. toc
+ -------
+ the following list shows the order in which all constructors are
+ defined. Use this list to locate code in this document:
- Global settings
- Global functions
+ Global settings
+ Global functions
- Color
- Point
- Rectangle
- Node
- Morph
- ShadowMorph
- HandleMorph
- PenMorph
- ColorPaletteMorph
- GrayPaletteMorph
- ColorPickerMorph
- BlinkerMorph
- CursorMorph
- BoxMorph
- SpeechBubbleMorph
- CircleBoxMorph
- SliderButtonMorph
- SliderMorph
- MouseSensorMorph*
- InspectorMorph
- MenuMorph
- StringMorph
- TextMorph
- TriggerMorph
- MenuItemMorph
- FrameMorph
- ScrollFrameMorph
- ListMorph
- StringFieldMorph
- BouncerMorph*
- HandMorph
- WorldMorph
+ Color
+ Point
+ Rectangle
+ Node
+ Morph
+ ShadowMorph
+ HandleMorph
+ PenMorph
+ ColorPaletteMorph
+ GrayPaletteMorph
+ ColorPickerMorph
+ BlinkerMorph
+ CursorMorph
+ BoxMorph
+ SpeechBubbleMorph
+ CircleBoxMorph
+ SliderButtonMorph
+ SliderMorph
+ MouseSensorMorph*
+ InspectorMorph
+ MenuMorph
+ StringMorph
+ TextMorph
+ TriggerMorph
+ MenuItemMorph
+ FrameMorph
+ ScrollFrameMorph
+ ListMorph
+ StringFieldMorph
+ BouncerMorph*
+ HandMorph
+ WorldMorph
- * included only for demo purposes
+ * included only for demo purposes
- III. yet to implement
- ---------------------
- - keyboard support for scroll frames and lists
+ III. yet to implement
+ ---------------------
+ - keyboard support for scroll frames and lists
+ - virtual keyboard support for Android and IE
- IV. open issues
- ----------------
- - blurry shadows don't work well in Chrome
+ IV. open issues
+ ----------------
+ - blurry shadows don't work well in Chrome
- V. browser compatibility
- ------------------------
- I have taken great care and considerable effort to make morphic.js
- runnable and appearing exactly the same on all current browsers
- available to me:
+ V. browser compatibility
+ ------------------------
+ I have taken great care and considerable effort to make morphic.js
+ runnable and appearing exactly the same on all current browsers
+ available to me:
- - Firefox for Windows
- - Firefox for Mac
- - Chrome for Windows (blurry shadows have some issues)
- - Chrome for Mac
- - Safari for Windows
- - safari for Mac
- - Safari for iOS (mobile)
- - IE for Windows
- - Opera for Windows
- - Opera for Mac
+ - Firefox for Windows
+ - Firefox for Mac
+ - Chrome for Windows (blurry shadows have some issues)
+ - Chrome for Mac
+ - Safari for Windows
+ - safari for Mac
+ - Safari for iOS (mobile)
+ - IE for Windows
+ - Opera for Windows
+ - Opera for Mac
- VI. the big picture
- -------------------
- Morphic.js is completely based on Canvas and JavaScript, it is just
- Morphic, nothing else. Morphic.js is very basic and covers only the
- bare essentials:
+ VI. the big picture
+ -------------------
+ Morphic.js is completely based on Canvas and JavaScript, it is just
+ Morphic, nothing else. Morphic.js is very basic and covers only the
+ bare essentials:
- * a stepping mechanism (a time-sharing multiplexer for lively
- user interaction ontop of a single OS/browser thread)
- * progressive display updates (only dirty rectangles are
- redrawn in each display cycle)
- * a tree structure
- * a single World per Canvas element (although you can have
- multiple worlds in multiple Canvas elements on the same web
- page)
- * a single Hand per World (but you can support multi-touch
- events)
- * a single text entry focus per World
+ * a stepping mechanism (a time-sharing multiplexer for lively
+ user interaction ontop of a single OS/browser thread)
+ * progressive display updates (only dirty rectangles are
+ redrawn in each display cycle)
+ * a tree structure
+ * a single World per Canvas element (although you can have
+ multiple worlds in multiple Canvas elements on the same web
+ page)
+ * a single Hand per World (but you can support multi-touch
+ events)
+ * a single text entry focus per World
- In its current state morphic.js doesn't support Transforms (you
- cannot rotate Morphs), but with PenMorph there already is a simple
- LOGO-like turtle that you can use to draw onto any Morph it is
- attached to. I'm planning to add special Morphs that support these
- operations later on, but not for every Morph in the system.
- Therefore these additions ("sprites" etc.) are likely to be part of
- other libraries ("microworld.js") in separate files.
+ In its current state morphic.js doesn't support Transforms (you
+ cannot rotate Morphs), but with PenMorph there already is a simple
+ LOGO-like turtle that you can use to draw onto any Morph it is
+ attached to. I'm planning to add special Morphs that support these
+ operations later on, but not for every Morph in the system.
+ Therefore these additions ("sprites" etc.) are likely to be part of
+ other libraries ("microworld.js") in separate files.
- the purpose of morphic.js is to provide a malleable framework that
- will let me experiment with lively GUIs for my hobby horse, which
- is drag-and-drop, blocks based programming languages. Those things
- (BYOB4 - http://byob.berkeley.edu) will be written using morphic.js
- as a library.
+ the purpose of morphic.js is to provide a malleable framework that
+ will let me experiment with lively GUIs for my hobby horse, which
+ is drag-and-drop, blocks based programming languages. Those things
+ (BYOB4 - http://byob.berkeley.edu) will be written using morphic.js
+ as a library.
- VII. programming guide
- ----------------------
- Morphic.js provides a library for lively GUIs inside single HTML
- Canvas elements. Each such canvas element functions as a "world" in
- which other visible shapes ("morphs") can be positioned and
- manipulated, often directly and interactively by the user. Morphs
- are tree nodes and may contain any number of submorphs ("children").
+ VII. programming guide
+ ----------------------
+ Morphic.js provides a library for lively GUIs inside single HTML
+ Canvas elements. Each such canvas element functions as a "world" in
+ which other visible shapes ("morphs") can be positioned and
+ manipulated, often directly and interactively by the user. Morphs
+ are tree nodes and may contain any number of submorphs ("children").
- All things visible in a morphic World are morphs themselves, i.e.
- all text rendering, blinking cursors, entry fields, menus, buttons,
- sliders, windows and dialog boxes etc. are created with morphic.js
- rather than using HTML DOM elements, and as a consequence can be
- changed and adjusted by the programmer regardless of proprietary
- browser behavior.
+ All things visible in a morphic World are morphs themselves, i.e.
+ all text rendering, blinking cursors, entry fields, menus, buttons,
+ sliders, windows and dialog boxes etc. are created with morphic.js
+ rather than using HTML DOM elements, and as a consequence can be
+ changed and adjusted by the programmer regardless of proprietary
+ browser behavior.
- Each World has an - invisible - "Hand" resembling the mouse cursor
- (or the user's finger on touch screens) which handles mouse events,
- and may also have a keyboardReceiver to handle key events.
+ Each World has an - invisible - "Hand" resembling the mouse cursor
+ (or the user's finger on touch screens) which handles mouse events,
+ and may also have a keyboardReceiver to handle key events.
- The basic idea of Morphic is to continuously run display cycles and
- to incrementally update the screen by only redrawing those World
- regions which have been "dirtied" since the last redraw. Before
- each shape is processed for redisplay it gets the chance to perform
- a "step" procedure, thus allowing for an illusion of concurrency.
+ The basic idea of Morphic is to continuously run display cycles and
+ to incrementally update the screen by only redrawing those World
+ regions which have been "dirtied" since the last redraw. Before
+ each shape is processed for redisplay it gets the chance to perform
+ a "step" procedure, thus allowing for an illusion of concurrency.
- (1) setting up a web page
- -------------------------
- Setting up a web page for Morphic always involves three steps:
- adding one or more Canvas elements, defining one or more worlds,
- initializing and starting the main loop.
+ (1) setting up a web page
+ -------------------------
+ Setting up a web page for Morphic always involves three steps:
+ adding one or more Canvas elements, defining one or more worlds,
+ initializing and starting the main loop.
- (a) single world
- -----------------
- Most commonly you will want your World to fill the browsers's whole
- client area. This default situation is easiest and most straight
- forward.
+ (a) single world
+ -----------------
+ Most commonly you will want your World to fill the browsers's whole
+ client area. This default situation is easiest and most straight
+ forward.
- example html file:
+ example html file:
- <!DOCTYPE html>
- <html>
- <head>
- <title>Morphic!</title>
- <script type="text/javascript" src="morphic.js"></script>
- <script type="text/javascript">
- var world;
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>Morphic!</title>
+ <script type="text/javascript" src="morphic.js"></script>
+ <script type="text/javascript">
+ var world;
- window.onload = function () {
- world = new WorldMorph(
- document.getElementById('world'));
- setInterval(loop, 50);
- };
+ window.onload = function () {
+ world = new WorldMorph(
+ document.getElementById('world'));
+ setInterval(loop, 50);
+ };
- function loop() {
- world.doOneCycle();
- }
- </script>
- </head>
- <body>
- <canvas id="world" tabindex="1" width="800" height="600">
- <p>Your browser doesn't support canvas.</p>
- </canvas>
- </body>
- </html>
+ function loop() {
+ world.doOneCycle();
+ }
+ </script>
+ </head>
+ <body>
+ <canvas id="world" tabindex="1" width="800" height="600">
+ <p>Your browser doesn't support canvas.</p>
+ </canvas>
+ </body>
+ </html>
- if you use ScrollFrames or otherwise plan to support mouse wheel
- scrolling events, you might also add the following inline-CSS
- attribute to the Canvas element:
+ if you use ScrollFrames or otherwise plan to support mouse wheel
+ scrolling events, you might also add the following inline-CSS
+ attribute to the Canvas element:
- style="position: absolute;"
+ style="position: absolute;"
- which will prevent the World to be scrolled around instead of the
- elements inside of it in some browsers.
+ which will prevent the World to be scrolled around instead of the
+ elements inside of it in some browsers.
- (b) multiple worlds
- -------------------
- If you wish to create a web page with more than one world, make
- sure to prevent each world from auto-filling the whole page and
- include it in the main loop. It's also a good idea to give each
- world its own tabindex:
+ (b) multiple worlds
+ -------------------
+ If you wish to create a web page with more than one world, make
+ sure to prevent each world from auto-filling the whole page and
+ include it in the main loop. It's also a good idea to give each
+ world its own tabindex:
- example html file:
+ example html file:
- <!DOCTYPE html>
- <html>
- <head>
- <title>Morphic!</title>
- <script type="text/javascript" src="morphic.js"></script>
- <script type="text/javascript">
- var world1, world2;
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>Morphic!</title>
+ <script type="text/javascript" src="morphic.js"></script>
+ <script type="text/javascript">
+ var world1, world2;
- window.onload = function () {
- world1 = new WorldMorph(
- document.getElementById('world1'), false);
- world2 = new WorldMorph(
- document.getElementById('world2'), false);
- setInterval(loop, 50);
- };
+ window.onload = function () {
+ world1 = new WorldMorph(
+ document.getElementById('world1'), false);
+ world2 = new WorldMorph(
+ document.getElementById('world2'), false);
+ setInterval(loop, 50);
+ };
- function loop() {
- world1.doOneCycle();
- world2.doOneCycle();
- }
- </script>
- </head>
- <body>
- <p>first world:</p>
- <canvas id="world1" tabindex="1" width="600" height="400">
- <p>Your browser doesn't support canvas.</p>
- </canvas>
- <p>second world:</p>
- <canvas id="world2" tabindex="2" width="400" height="600">
- <p>Your browser doesn't support canvas.</p>
- </canvas>
- </body>
- </html>
+ function loop() {
+ world1.doOneCycle();
+ world2.doOneCycle();
+ }
+ </script>
+ </head>
+ <body>
+ <p>first world:</p>
+ <canvas id="world1" tabindex="1" width="600" height="400">
+ <p>Your browser doesn't support canvas.</p>
+ </canvas>
+ <p>second world:</p>
+ <canvas id="world2" tabindex="2" width="400" height="600">
+ <p>Your browser doesn't support canvas.</p>
+ </canvas>
+ </body>
+ </html>
- (c) an application
- -------------------
- Of course, most of the time you don't want to just plain use the
- standard Morhic World "as is" out of the box, but write your own
- application (something like Scratch!) in it. For such an
- application you'll create your own morph prototypes, perhaps
- assemble your own "window frame" and bring it all to life in a
- customized World state. the following example creates a simple
- snake-like mouse drawing game.
+ (c) an application
+ -------------------
+ Of course, most of the time you don't want to just plain use the
+ standard Morhic World "as is" out of the box, but write your own
+ application (something like Scratch!) in it. For such an
+ application you'll create your own morph prototypes, perhaps
+ assemble your own "window frame" and bring it all to life in a
+ customized World state. the following example creates a simple
+ snake-like mouse drawing game.
- example html file:
+ example html file:
- <!DOCTYPE html>
- <html>
- <head>
- <title>touch me!</title>
- <script type="text/javascript" src="morphic.js"></script>
- <script type="text/javascript">
- var worldCanvas, sensor;
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>touch me!</title>
+ <script type="text/javascript" src="morphic.js"></script>
+ <script type="text/javascript">
+ var worldCanvas, sensor;
- window.onload = function () {
- var x, y, w, h;
+ window.onload = function () {
+ var x, y, w, h;
- worldCanvas = document.getElementById('world');
- world = new WorldMorph(worldCanvas);
- world.isDevMode = false;
- world.color = new Color();
+ worldCanvas = document.getElementById('world');
+ world = new WorldMorph(worldCanvas);
+ world.isDevMode = false;
+ world.color = new Color();
- w = 100;
- h = 100;
+ w = 100;
+ h = 100;
- x = 0;
- y = 0;
+ x = 0;
+ y = 0;
- while ((y * h) < world.height()) {
- while ((x * w) < world.width()) {
- sensor = new MouseSensorMorph();
- sensor.setPosition(new Point(x * w, y * h));
- sensor.alpha = 0;
- sensor.setExtent(new Point(w, h));
- world.add(sensor);
- x += 1;
- }
- x = 0;
- y += 1;
- }
- setInterval(loop, 50);
- };
+ while ((y * h) < world.height()) {
+ while ((x * w) < world.width()) {
+ sensor = new MouseSensorMorph();
+ sensor.setPosition(new Point(x * w, y * h));
+ sensor.alpha = 0;
+ sensor.setExtent(new Point(w, h));
+ world.add(sensor);
+ x += 1;
+ }
+ x = 0;
+ y += 1;
+ }
+ setInterval(loop, 50);
+ };
- function loop() {
- world.doOneCycle();
- }
- </script>
- </head>
- <body bgcolor='black'>
- <canvas id="world" width="800" height="600">
- <p>Your browser doesn't support canvas.</p>
- </canvas>
- </body>
- </html>
+ function loop() {
+ world.doOneCycle();
+ }
+ </script>
+ </head>
+ <body bgcolor='black'>
+ <canvas id="world" width="800" height="600">
+ <p>Your browser doesn't support canvas.</p>
+ </canvas>
+ </body>
+ </html>
- To get an idea how you can craft your own custom morph prototypes
- I've included two examples which should give you an idea how to add
- properties, override inherited methods and use the stepping
- mechanism for "livelyness":
+ To get an idea how you can craft your own custom morph prototypes
+ I've included two examples which should give you an idea how to add
+ properties, override inherited methods and use the stepping
+ mechanism for "livelyness":
- BouncerMorph
- MouseSensorMorph
+ BouncerMorph
+ MouseSensorMorph
- For the sake of sharing a single file I've included those examples
- in morphic.js itself. Usually you'll define your additions in a
- separate file and keep morphic.js untouched.
+ For the sake of sharing a single file I've included those examples
+ in morphic.js itself. Usually you'll define your additions in a
+ separate file and keep morphic.js untouched.
- (2) manipulating morphs
- -----------------------
- There are many methods to programmatically manipulate morphs. Among
- the most important and common ones among all morphs are the
- following nine:
+ (2) manipulating morphs
+ -----------------------
+ There are many methods to programmatically manipulate morphs. Among
+ the most important and common ones among all morphs are the
+ following nine:
- * hide()
- * show()
+ * hide()
+ * show()
- * setPosition(aPoint)
- * setExtent(aPoint)
- * setColor(aColor)
+ * setPosition(aPoint)
+ * setExtent(aPoint)
+ * setColor(aColor)
- * add(submorph) - attaches submorph ontop
- * addBack(submorph) - attaches submorph underneath
+ * add(submorph) - attaches submorph ontop
+ * addBack(submorph) - attaches submorph underneath
- * fullCopy() - duplication
- * destroy() - deletion
+ * fullCopy() - duplication
+ * destroy() - deletion
- (3) events
- ----------
- All user (and system) interaction is triggered by events, which are
- passed on from the root element - the World - to its submorphs. The
- World contains a list of system (browser) events it reacts to in its
+ (3) events
+ ----------
+ All user (and system) interaction is triggered by events, which are
+ passed on from the root element - the World - to its submorphs. The
+ World contains a list of system (browser) events it reacts to in its
- initEventListeners()
+ initEventListeners()
- method. Currently there are
+ method. Currently there are
- - mouse
- - drop
- - keyboard
- - (window) resize
+ - mouse
+ - drop
+ - keyboard
+ - (window) resize
- events.
+ events.
- These system events are dispatched within the morphic World by the
- World's Hand and its keyboardReceiver (usually the active text
- cursor).
+ These system events are dispatched within the morphic World by the
+ World's Hand and its keyboardReceiver (usually the active text
+ cursor).
- (a) mouse events:
- -----------------
- The Hand dispatches the following mouse events to relevant morphs:
+ (a) mouse events:
+ -----------------
+ The Hand dispatches the following mouse events to relevant morphs:
- mouseDownLeft
- mouseDownRight
- mouseClickLeft
- mouseClickRight
- mouseEnter
- mouseLeave
- mouseEnterDragging
- mouseLeaveDragging
- mouseMove
- mouseScroll
+ mouseDownLeft
+ mouseDownRight
+ mouseClickLeft
+ mouseClickRight
+ mouseDoubleClick
+ mouseEnter
+ mouseLeave
+ mouseEnterDragging
+ mouseLeaveDragging
+ mouseMove
+ mouseScroll
- If you wish your morph to react to any such event, simply add a
- method of the same name as the event, e.g:
+ If you wish your morph to react to any such event, simply add a
+ method of the same name as the event, e.g:
- MyMorph.prototype.mouseMove = function(pos) {};
+ MyMorph.prototype.mouseMove = function(pos) {};
- The only optional parameter of such a method is a Point object
- indicating the current position of the Hand inside the World's
- coordinate system.
+ The only optional parameter of such a method is a Point object
+ indicating the current position of the Hand inside the World's
+ coordinate system.
- Events may be "bubbled" up a morph's owner chain by calling
+ Events may be "bubbled" up a morph's owner chain by calling
- this.escalateEvent(functionName, arg)
+ this.escalateEvent(functionName, arg)
- in the event handler method's code.
+ in the event handler method's code.
- Likewise, removing the event handler method will render your morph
- passive to the event in question.
+ Likewise, removing the event handler method will render your morph
+ passive to the event in question.
- (b) context menu:
- -----------------
- By default right-clicking (or single-finger tap-and-hold) on a morph
- also invokes its context menu (in addition to firing the
- mouseClickRight event). A morph's context menu can be customized by
- assigning a Menu instance to its
+ (b) context menu:
+ -----------------
+ By default right-clicking (or single-finger tap-and-hold) on a morph
+ also invokes its context menu (in addition to firing the
+ mouseClickRight event). A morph's context menu can be customized by
+ assigning a Menu instance to its
- customContextMenu
+ customContextMenu
- property, or altogether suppressed by overriding its inherited
+ property, or altogether suppressed by overriding its inherited
- contextMenu()
+ contextMenu()
- method.
+ method.
- (c) dragging:
- -------------
- Dragging a morph is initiated when the left mouse button is pressed,
- held and the mouse is moved.
+ (c) dragging:
+ -------------
+ Dragging a morph is initiated when the left mouse button is pressed,
+ held and the mouse is moved.
- You can control whether a morph is draggable by setting its
+ You can control whether a morph is draggable by setting its
- isDraggable
+ isDraggable
- property either to false or true. If a morph isn't draggable itself
- it will pass the pick-up request up its owner chain. This lets you
- create draggable composite morphs like Windows, DialogBoxes,
- Sliders etc.
+ property either to false or true. If a morph isn't draggable itself
+ it will pass the pick-up request up its owner chain. This lets you
+ create draggable composite morphs like Windows, DialogBoxes,
+ Sliders etc.
- Sometimes it is desireable to make "template" shapes which cannot be
- moved themselves, but from which instead duplicates can be peeled
- off. This is especially useful for building blocks in construction
- kits, e.g. the MIT-Scratch palette. Morphic.js lets you control this
- functionality by setting the
+ Sometimes it is desireable to make "template" shapes which cannot be
+ moved themselves, but from which instead duplicates can be peeled
+ off. This is especially useful for building blocks in construction
+ kits, e.g. the MIT-Scratch palette. Morphic.js lets you control this
+ functionality by setting the
- isTemplate
+ isTemplate
- property flag to true for any morph whose "isDraggable" property is
- turned off. When dragging such a Morph the hand will instead grab
- a duplicate of the template whose "isDraggable" flag is true and
- whose "isTemplate" flag is false, in other words: a non-template.
+ property flag to true for any morph whose "isDraggable" property is
+ turned off. When dragging such a Morph the hand will instead grab
+ a duplicate of the template whose "isDraggable" flag is true and
+ whose "isTemplate" flag is false, in other words: a non-template.
- Dragging is indicated by adding a drop shadow to the morph in hand.
- If a morph follows the hand without displaying a drop shadow it is
- merely being moved about without changing its parent (owner morph),
- e.g. when "dragging" a morph handle to resize its owner, or when
- "dragging" a slider button.
+ Dragging is indicated by adding a drop shadow to the morph in hand.
+ If a morph follows the hand without displaying a drop shadow it is
+ merely being moved about without changing its parent (owner morph),
+ e.g. when "dragging" a morph handle to resize its owner, or when
+ "dragging" a slider button.
- Right before a morph is picked up its
+ Right before a morph is picked up its
- prepareToBeGrabbed(handMorph)
+ prepareToBeGrabbed(handMorph)
- method is invoked, if it is present. Immediately after the pick-up
- the former parent's
+ method is invoked, if it is present. Immediately after the pick-up
+ the former parent's
- reactToGrabOf(grabbedMorph)
+ reactToGrabOf(grabbedMorph)
- method is called, again only if it exists.
+ method is called, again only if it exists.
- Similar to events, these methods are optional and don't exist by
- default. For a simple example of how they can be used to adjust
- scroll bars in a scroll frame please have a look at their
- implementation in FrameMorph.
+ Similar to events, these methods are optional and don't exist by
+ default. For a simple example of how they can be used to adjust
+ scroll bars in a scroll frame please have a look at their
+ implementation in FrameMorph.
- (d) dropping:
- -------------
- Dropping is triggered when the left mouse button is either pressed
- or released while the Hand is dragging a morph.
+ (d) dropping:
+ -------------
+ Dropping is triggered when the left mouse button is either pressed
+ or released while the Hand is dragging a morph.
- Dropping a morph causes it to become embedded in a new owner morph.
- You can control this embedding behavior by setting the prospective
- drop target's
+ Dropping a morph causes it to become embedded in a new owner morph.
+ You can control this embedding behavior by setting the prospective
+ drop target's
- acceptsDrops
+ acceptsDrops
- property to either true or false, or by overriding its inherited
+ property to either true or false, or by overriding its inherited
- wantsDropOf(aMorph)
+ wantsDropOf(aMorph)
- method.
+ method.
- Right after a morph has been dropped its
+ Right after a morph has been dropped its
- justDropped(handMorph)
+ justDropped(handMorph)
- method is called, and its new parent's
+ method is called, and its new parent's
- reactToDropOf(droppedMorph, handMorph)
+ reactToDropOf(droppedMorph, handMorph)
- method is invoked, again only if each method exists.
+ method is invoked, again only if each method exists.
- Similar to events, these methods are optional and by default are
- not present in morphs by default (watch out for inheritance,
- though!). For a simple example of how they can be used to adjust
- scroll bars in a scroll frame please have a look at their
- implementation in FrameMorph.
+ Similar to events, these methods are optional and by default are
+ not present in morphs by default (watch out for inheritance,
+ though!). For a simple example of how they can be used to adjust
+ scroll bars in a scroll frame please have a look at their
+ implementation in FrameMorph.
- Drops of image elements from outside the world canvas are dispatched as
+ Drops of image elements from outside the world canvas are dispatched as
- droppedImage(aCanvas, name)
+ droppedImage(aCanvas, name)
+ droppedSVG(anImage, name)
- events to interested Morphs at the mouse pointer. If you want you Morph
- to e.g. import outside images you can add the droppedImage() method to
- it. The parameter passed to the event handles is a new offscreen
- canvas element representing a copy of the original image element which
- can be directly used, e.g. by assigning it to another Morph's image
- property.
+ events to interested Morphs at the mouse pointer. If you want you Morph
+ to e.g. import outside images you can add the droppedImage() and / or the
+ droppedSVG() methods to it. The parameter passed to the event handles is
+ a new offscreen canvas element representing a copy of the original image
+ element which can be directly used, e.g. by assigning it to another
+ Morph's image property. In the case of a dropped SVG it is an image
+ element (not a canvas), which has to be rasterized onto a canvas before
+ it can be used. The benefit of handling SVGs as image elements is that
+ rasterization can be deferred until the destination scale is known, taking
+ advantage of SVG's ability for smooth scaling. If instead SVGs are to be
+ rasterized right away, you can set the
- The same applies to drops of audio or text files from outside the world
- canvas.
+ MorphicPreferences.rasterizeSVGs
- Those are dispatched as
+ preference to <true>. In this case dropped SVGs also trigger the
+ droppedImage() event with a canvas containing a rasterized version of the
+ SVG.
- droppedAudio(anAudio, name)
- droppedText(aString, name)
+ The same applies to drops of audio or text files from outside the world
+ canvas.
- events to interested Morphs at the mouse pointer.
+ Those are dispatched as
+ droppedAudio(anAudio, name)
+ droppedText(aString, name)
- (e) keyboard events
- -------------------
- The World dispatches the following key events to its active
- keyboardReceiver:
+ events to interested Morphs at the mouse pointer.
- keypress
- keydown
+ if none of the above content types can be determined, the file contents
+ is dispatched as an ArrayBuffer to interested Morphs:
+
+ droppedBinary(anArrayBuffer, name)
+
+
+ (e) keyboard events
+ -------------------
+ The World dispatches the following key events to its active
+ keyboardReceiver:
+
+ keypress
+ keydown
keyup
- Currently the only morph which acts as keyboard receiver is
- CursorMorph, the basic text editing widget. If you wish to add
- keyboard support to your morph you need to add event handling
- methods for
+ Currently the only morph which acts as keyboard receiver is
+ CursorMorph, the basic text editing widget. If you wish to add
+ keyboard support to your morph you need to add event handling
+ methods for
- processKeyPress(event)
- processKeyDown(event)
- processKeyUp(event)
+ processKeyPress(event)
+ processKeyDown(event)
+ processKeyUp(event)
- and activate them by assigning your morph to the World's
+ and activate them by assigning your morph to the World's
- keyboardReceiver
+ keyboardReceiver
- property.
+ property.
- Note that processKeyUp() is optional and doesn't have to be present
- if your morph doesn't require it.
+ Note that processKeyUp() is optional and doesn't have to be present
+ if your morph doesn't require it.
- (f) resize event
- ----------------
- The Window resize event is handled by the World and allows the
- World's extent to be adjusted so that it always completely fills
- the browser's visible page. You can turn off this default behavior
- by setting the World's
+ (f) resize event
+ ----------------
+ The Window resize event is handled by the World and allows the
+ World's extent to be adjusted so that it always completely fills
+ the browser's visible page. You can turn off this default behavior
+ by setting the World's
- useFillPage
+ useFillPage
- property to false.
+ property to false.
- Alternatively you can also initialize the World with the
- useFillPage switch turned off from the beginning by passing the
- false value as second parameter to the World's constructor:
+ Alternatively you can also initialize the World with the
+ useFillPage switch turned off from the beginning by passing the
+ false value as second parameter to the World's constructor:
- world = new World(aCanvas, false);
+ world = new World(aCanvas, false);
- Use this when creating a web page with multiple Worlds.
+ Use this when creating a web page with multiple Worlds.
- if "useFillPage" is turned on the World dispatches an
+ if "useFillPage" is turned on the World dispatches an
- reactToWorldResize(newBounds)
+ reactToWorldResize(newBounds)
- events to all of its children (toplevel only), allowing each to
- adjust to the new World bounds by implementing a corresponding
- method, the passed argument being the World's new dimensions after
- completing the resize. By default, the "reactToWorldResize" Method
- does not exist.
+ events to all of its children (toplevel only), allowing each to
+ adjust to the new World bounds by implementing a corresponding
+ method, the passed argument being the World's new dimensions after
+ completing the resize. By default, the "reactToWorldResize" Method
+ does not exist.
- Example:
+ Example:
- Add the following method to your Morph to let it automatically
- fill the whole World, but leave a 10 pixel border uncovered:
+ Add the following method to your Morph to let it automatically
+ fill the whole World, but leave a 10 pixel border uncovered:
- MyMorph.prototype.reactToWorldResize = function (rect) {
- this.changed();
- this.bounds = rect.insetBy(10);
- this.drawNew();
- this.changed();
- };
+ MyMorph.prototype.reactToWorldResize = function (rect) {
+ this.changed();
+ this.bounds = rect.insetBy(10);
+ this.drawNew();
+ this.changed();
+ };
- (g) combined mouse-keyboard events
- ----------------------------------
- Occasionally you'll want an object to react differently to a mouse
- click or to some other mouse event while the user holds down a key
- on the keyboard. Such "shift-click", "ctl-click", or "alt-click"
- events can be implemented by querying the World's
+ (g) combined mouse-keyboard events
+ ----------------------------------
+ Occasionally you'll want an object to react differently to a mouse
+ click or to some other mouse event while the user holds down a key
+ on the keyboard. Such "shift-click", "ctl-click", or "alt-click"
+ events can be implemented by querying the World's
- currentKey
+ currentKey
- property inside the function that reacts to the mouse event. This
- property stores the keyCode of the key that's currently pressed.
- Once the key is released by the user it reverts to null.
+ property inside the function that reacts to the mouse event. This
+ property stores the keyCode of the key that's currently pressed.
+ Once the key is released by the user it reverts to null.
- (h) text editing events
- -----------------------
- Much of Morphic's "liveliness" comes out of allowing text elements
- (instances of either single-lined StringMorph or multi-lined TextMorph)
- to be directly manipulated and edited by users. This requires other
- objects which may have an interest in the text element's state to react
- appropriately. Therefore text elements and their manipulators emit
- a stream of events, mostly by "bubbling" them up the text element's
- owner chain. Text elements' parents are notified about the following
- events:
+ (h) text editing events
+ -----------------------
+ Much of Morphic's "liveliness" comes out of allowing text elements
+ (instances of either single-lined StringMorph or multi-lined TextMorph)
+ to be directly manipulated and edited by users. This requires other
+ objects which may have an interest in the text element's state to react
+ appropriately. Therefore text elements and their manipulators emit
+ a stream of events, mostly by "bubbling" them up the text element's
+ owner chain. Text elements' parents are notified about the following
+ events:
- Whenever the user presses a key on the keyboard while a text element
- is being edited, a
+ Whenever the user presses a key on the keyboard while a text element
+ is being edited, a
- reactToKeystroke(event)
+ reactToKeystroke(event)
- is escalated up its parent chain, the "event" parameter being the
- original one received by the World.
+ is escalated up its parent chain, the "event" parameter being the
+ original one received by the World.
- Once the user has completed the edit, the following events are
- dispatched:
+ Once the user has completed the edit, the following events are
+ dispatched:
- accept() - <enter> was pressed on a single line of text
- cancel() - <esc> was pressed on any text element
+ accept() - <enter> was pressed on a single line of text
+ cancel() - <esc> was pressed on any text element
- Note that "accept" only gets triggered by single-line texte elements,
- as the <enter> key is used to insert line breaks in multi-line
- elements. Therefore, whenever a text edit is terminated by the user
- (accepted, cancelled or otherwise),
+ Note that "accept" only gets triggered by single-line texte elements,
+ as the <enter> key is used to insert line breaks in multi-line
+ elements. Therefore, whenever a text edit is terminated by the user
+ (accepted, cancelled or otherwise),
- reactToEdit(StringOrTextMorph)
+ reactToEdit(StringOrTextMorph)
- is triggered.
+ is triggered.
- If the MorphicPreference's
+ If the MorphicPreference's
- useSliderForInput
+ useSliderForInput
- setting is turned on, a slider is popped up underneath the currently
- edited text element letting the user insert numbers out of the given
- slider range. Whenever this happens, i.e. whenever the slider is moved
- or while the slider button is pressed, a stream of
+ setting is turned on, a slider is popped up underneath the currently
+ edited text element letting the user insert numbers out of the given
+ slider range. Whenever this happens, i.e. whenever the slider is moved
+ or while the slider button is pressed, a stream of
- reactToSliderEdit(StringOrTextMorph)
+ reactToSliderEdit(StringOrTextMorph)
- events is dispatched, allowing for "Bret-Victor" style "live coding"
- applications.
+ events is dispatched, allowing for "Bret-Victor" style "live coding"
+ applications.
- In addition to user-initiated events text elements also emit
- change notifications to their direct parents whenever their drawNew()
- method is invoked. That way complex Morphs containing text elements
- get a chance to react if something about the embedded text has been
- modified programmatically. These events are:
+ In addition to user-initiated events text elements also emit
+ change notifications to their direct parents whenever their drawNew()
+ method is invoked. That way complex Morphs containing text elements
+ get a chance to react if something about the embedded text has been
+ modified programmatically. These events are:
- layoutChanged() - sent from instances of TextMorph
- fixLayout() - sent from instances of StringMorph
+ layoutChanged() - sent from instances of TextMorph
+ fixLayout() - sent from instances of StringMorph
- they are different so that Morphs which contain both multi-line and
- single-line text elements can hold them apart.
+ they are different so that Morphs which contain both multi-line and
+ single-line text elements can hold them apart.
- (4) stepping
- ------------
- Stepping is what makes Morphic "magical". Two properties control
- a morph's stepping behavior: the fps attribute and the step()
- method.
+ (4) stepping
+ ------------
+ Stepping is what makes Morphic "magical". Two properties control
+ a morph's stepping behavior: the fps attribute and the step()
+ method.
- By default the
+ By default the
- step()
+ step()
- method does nothing. As you can see in the examples of BouncerMorph
- and MouseSensorMorph you can easily override this inherited method
- to suit your needs.
+ method does nothing. As you can see in the examples of BouncerMorph
+ and MouseSensorMorph you can easily override this inherited method
+ to suit your needs.
- By default the step() method is called once per display cycle.
- Depending on the number of actively stepping morphs and the
- complexity of your step() methods this can cause quite a strain on
- your CPU, and also result in your application behaving differently
- on slower computers than on fast ones.
+ By default the step() method is called once per display cycle.
+ Depending on the number of actively stepping morphs and the
+ complexity of your step() methods this can cause quite a strain on
+ your CPU, and also result in your application behaving differently
+ on slower computers than on fast ones.
- setting
+ setting
- myMorph.fps
+ myMorph.fps
- to a number lower than the interval for the main loop lets you free
- system resources (albeit at the cost of a less responsive or slower
- behavior for this particular morph).
+ to a number lower than the interval for the main loop lets you free
+ system resources (albeit at the cost of a less responsive or slower
+ behavior for this particular morph).
- (5) creating new kinds of morphs
- --------------------------------
- The real fun begins when you start to create new kinds of morphs
- with customized shapes. Imagine, e.g. jigsaw puzzle pieces or
- musical notes. For this you have to override the default
+ (5) creating new kinds of morphs
+ --------------------------------
+ The real fun begins when you start to create new kinds of morphs
+ with customized shapes. Imagine, e.g. jigsaw puzzle pieces or
+ musical notes. For this you have to override the default
- drawNew()
+ drawNew()
- method.
+ method.
- This method creates a new offscreen Canvas and stores it in
- the morph's
+ This method creates a new offscreen Canvas and stores it in
+ the morph's
- image
+ image
- property.
+ property.
- Use the following template for a start:
+ Use the following template for a start:
- MyMorph.prototype.drawNew = function() {
- var context;
- this.image = newCanvas(this.extent());
- context = this.image.getContext('2d');
- // use context to paint stuff here
- };
+ MyMorph.prototype.drawNew = function() {
+ var context;
+ this.image = newCanvas(this.extent());
+ context = this.image.getContext('2d');
+ // use context to paint stuff here
+ };
- If your new morph stores or references other morphs outside of the
- submorph tree in other properties, be sure to also override the
- default
+ If your new morph stores or references other morphs outside of the
+ submorph tree in other properties, be sure to also override the
+ default
- copyRecordingReferences()
+ copyRecordingReferences()
- method accordingly if you want it to support duplication.
+ method accordingly if you want it to support duplication.
- (6) development and user modes
- ------------------------------
- When working with Squeak on Scratch or BYOB among the features I
- like the best and use the most is inspecting what's going on in
- the World while it is up and running. That's what development mode
- is for (you could also call it debug mode). In essence development
- mode controls which context menu shows up. In user mode right
- clicking (or double finger tapping) a morph invokes its
+ (6) development and user modes
+ ------------------------------
+ When working with Squeak on Scratch or BYOB among the features I
+ like the best and use the most is inspecting what's going on in
+ the World while it is up and running. That's what development mode
+ is for (you could also call it debug mode). In essence development
+ mode controls which context menu shows up. In user mode right
+ clicking (or double finger tapping) a morph invokes its
- customContextMenu
+ customContextMenu
- property, whereas in development mode only the general
+ property, whereas in development mode only the general
- developersMenu()
+ developersMenu()
- method is called and the resulting menu invoked. The developers'
- menu features Gui-Builder-wise functionality to directly inspect,
- take apart, reassamble and otherwise manipulate morphs and their
- contents.
+ method is called and the resulting menu invoked. The developers'
+ menu features Gui-Builder-wise functionality to directly inspect,
+ take apart, reassamble and otherwise manipulate morphs and their
+ contents.
- Instead of using the "customContextMenu" property you can also
- assign a more dynamic contextMenu by overriding the general
+ Instead of using the "customContextMenu" property you can also
+ assign a more dynamic contextMenu by overriding the general
- userMenu()
+ userMenu()
- method with a customized menu constructor. The difference between
- the customContextMenu property and the userMenu() method is that
- the former is also present in development mode and overrides the
- developersMenu() result. For an example of how to use the
- customContextMenu property have a look at TextMorph's evaluation
- menu, which is used for the Inspector's evaluation pane.
+ method with a customized menu constructor. The difference between
+ the customContextMenu property and the userMenu() method is that
+ the former is also present in development mode and overrides the
+ developersMenu() result. For an example of how to use the
+ customContextMenu property have a look at TextMorph's evaluation
+ menu, which is used for the Inspector's evaluation pane.
- When in development mode you can inspect every Morph's properties
- with the inspector, including all of its methods. The inspector
- also lets you add, remove and rename properties, and even edit
- their values at runtime. Like in a Smalltalk environment the inspect
- features an evaluation pane into which you can type in arbitrary
- JavaScript code and evaluate it in the context of the inspectee.
+ When in development mode you can inspect every Morph's properties
+ with the inspector, including all of its methods. The inspector
+ also lets you add, remove and rename properties, and even edit
+ their values at runtime. Like in a Smalltalk environment the inspect
+ features an evaluation pane into which you can type in arbitrary
+ JavaScript code and evaluate it in the context of the inspectee.
- Use switching between user and development modes while you are
- developing an application and disable switching to development once
- you're done and deploying, because generally you don't want to
- confuse end-users with inspectors and meta-level stuff.
+ Use switching between user and development modes while you are
+ developing an application and disable switching to development once
+ you're done and deploying, because generally you don't want to
+ confuse end-users with inspectors and meta-level stuff.
- (7) turtle graphics
- -------------------
+ (7) turtle graphics
+ -------------------
- The basic Morphic kernel features a simple LOGO turtle constructor
- called
+ The basic Morphic kernel features a simple LOGO turtle constructor
+ called
- PenMorph
+ PenMorph
- which you can use to draw onto its parent Morph. By default every
- Morph in the system (including the World) is able to act as turtle
- canvas and can display pen trails. Pen trails will be lost whenever
- the trails morph (the pen's parent) performs a "drawNew()"
- operation. If you want to create your own pen trails canvas, you
- may wish to modify its
+ which you can use to draw onto its parent Morph. By default every
+ Morph in the system (including the World) is able to act as turtle
+ canvas and can display pen trails. Pen trails will be lost whenever
+ the trails morph (the pen's parent) performs a "drawNew()"
+ operation. If you want to create your own pen trails canvas, you
+ may wish to modify its
- penTrails()
+ penTrails()
- property, so that it keeps a separate offscreen canvas for pen
- trails (and doesn't loose these on redraw).
+ property, so that it keeps a separate offscreen canvas for pen
+ trails (and doesn't loose these on redraw).
- the following properties of PenMorph are relevant for turtle
- graphics:
+ the following properties of PenMorph are relevant for turtle
+ graphics:
- color - a Color
- size - line width of pen trails
- heading - degrees
- isDown - drawing state
+ color - a Color
+ size - line width of pen trails
+ heading - degrees
+ isDown - drawing state
- the following commands can be used to actually draw something:
+ the following commands can be used to actually draw something:
- up() - lift the pen up, further movements leave no trails
- down() - set down, further movements leave trails
- clear() - remove all trails from the current parent
- forward(n) - move n steps in the current direction (heading)
- turn(n) - turn right n degrees
+ up() - lift the pen up, further movements leave no trails
+ down() - set down, further movements leave trails
+ clear() - remove all trails from the current parent
+ forward(n) - move n steps in the current direction (heading)
+ turn(n) - turn right n degrees
- Turtle graphics can best be explored interactively by creating a
- new PenMorph object and by manipulating it with the inspector
- widget.
+ Turtle graphics can best be explored interactively by creating a
+ new PenMorph object and by manipulating it with the inspector
+ widget.
- NOTE: PenMorph has a special optimization for recursive operations
- called
+ NOTE: PenMorph has a special optimization for recursive operations
+ called
- warp(function)
+ warp(function)
- You can significantly speed up recursive ops and increase the depth
- of recursion that's displayable by wrapping WARP around your
- recursive function call:
+ You can significantly speed up recursive ops and increase the depth
+ of recursion that's displayable by wrapping WARP around your
+ recursive function call:
- example:
+ example:
- myPen.warp(function () {
- myPen.tree(12, 120, 20);
- })
+ myPen.warp(function () {
+ myPen.tree(12, 120, 20);
+ })
- will be much faster than just invoking the tree function, because it
- prevents the parent's parent from keeping track of every single line
- segment and instead redraws the outcome in a single pass.
+ will be much faster than just invoking the tree function, because it
+ prevents the parent's parent from keeping track of every single line
+ segment and instead redraws the outcome in a single pass.
- (8) damage list housekeeping
- ----------------------------
- Morphic's progressive display update comes at the cost of having to
- cycle through a list of "broken rectangles" every display cycle. If
- this list gets very long working this damage list can lead to a
- seemingly dramatic slow-down of the Morphic system. Typically this
- occurs when updating the layout of complex Morphs with very many
- submorphs, e.g. when resizing an inspector window.
+ (8) damage list housekeeping
+ ----------------------------
+ Morphic's progressive display update comes at the cost of having to
+ cycle through a list of "broken rectangles" every display cycle. If
+ this list gets very long working this damage list can lead to a
+ seemingly dramatic slow-down of the Morphic system. Typically this
+ occurs when updating the layout of complex Morphs with very many
+ submorphs, e.g. when resizing an inspector window.
- An effective strategy to cope with this is to use the inherited
+ An effective strategy to cope with this is to use the inherited
- trackChanges
+ trackChanges
- property of the Morph prototype for damage list housekeeping.
+ property of the Morph prototype for damage list housekeeping.
- The trackChanges property of the Morph prototype is a Boolean switch
- that determines whether the World's damage list ('broken' rectangles)
- tracks changes. By default the switch is always on. If set to false
- changes are not stored. This can be very useful for housekeeping of
- the damage list in situations where a large number of (sub-) morphs
- are changed more or less at once. Instead of keeping track of every
- single submorph's changes tremendous performance improvements can be
- achieved by setting the trackChanges flag to false before propagating
- the layout changes, setting it to true again and then storing the full
- bounds of the surrounding morph. An an example refer to the
+ The trackChanges property of the Morph prototype is a Boolean switch
+ that determines whether the World's damage list ('broken' rectangles)
+ tracks changes. By default the switch is always on. If set to false
+ changes are not stored. This can be very useful for housekeeping of
+ the damage list in situations where a large number of (sub-) morphs
+ are changed more or less at once. Instead of keeping track of every
+ single submorph's changes tremendous performance improvements can be
+ achieved by setting the trackChanges flag to false before propagating
+ the layout changes, setting it to true again and then storing the full
+ bounds of the surrounding morph. An an example refer to the
- moveBy()
+ moveBy()
- method of HandMorph, and to the
+ method of HandMorph, and to the
- fixLayout()
+ fixLayout()
- method of InspectorMorph, or the
+ method of InspectorMorph, or the
- startLayout()
- endLayout()
+ startLayout()
+ endLayout()
- methods of SyntaxElementMorph in the Snap application.
+ methods of SyntaxElementMorph in the Snap application.
- (9) minifying morphic.js
- ------------------------
- Coming from Smalltalk and being a Squeaker at heart I am a huge fan
- of browsing the code itself to make sense of it. Therefore I have
- included this documentation and (too little) inline comments so all
- you need to get going is this very file.
+ (9) minifying morphic.js
+ ------------------------
+ Coming from Smalltalk and being a Squeaker at heart I am a huge fan
+ of browsing the code itself to make sense of it. Therefore I have
+ included this documentation and (too little) inline comments so all
+ you need to get going is this very file.
- Nowadays with live streaming HD video even on mobile phones 250 KB
- shouldn't be a big strain on bandwith, still minifying and even
- compressing morphic.js down do about 100 KB may sometimes improve
- performance in production use.
+ Nowadays with live streaming HD video even on mobile phones 250 KB
+ shouldn't be a big strain on bandwith, still minifying and even
+ compressing morphic.js down do about 100 KB may sometimes improve
+ performance in production use.
- Being an attorney-at-law myself you programmer folk keep harassing
- me with rabulistic nitpickings about free software licenses. I'm
- releasing morphic.js under an AGPL license. Therefore please make
- sure to adhere to that license in any minified or compressed version.
+ Being an attorney-at-law myself you programmer folk keep harassing
+ me with rabulistic nitpickings about free software licenses. I'm
+ releasing morphic.js under an AGPL license. Therefore please make
+ sure to adhere to that license in any minified or compressed version.
- VIII. acknowledgements
- ----------------------
- The original Morphic was designed and written by Randy Smith and
- John Maloney for the SELF programming language, and later ported to
- Squeak (Smalltalk) by John Maloney and Dan Ingalls, who has also
- ported it to JavaScript (the Lively Kernel), once again setting
- a "Gold Standard" for self sustaining systems which morphic.js
- cannot and does not aspire to meet.
+ VIII. acknowledgements
+ ----------------------
+ The original Morphic was designed and written by Randy Smith and
+ John Maloney for the SELF programming language, and later ported to
+ Squeak (Smalltalk) by John Maloney and Dan Ingalls, who has also
+ ported it to JavaScript (the Lively Kernel), once again setting
+ a "Gold Standard" for self sustaining systems which morphic.js
+ cannot and does not aspire to meet.
- This Morphic implementation for JavaScript is not a direct port of
- Squeak's Morphic, but still many individual functions have been
- ported almost literally from Squeak, sometimes even including their
- comments, e.g. the morph duplication mechanism fullCopy(). Squeak
- has been a treasure trove, and if morphic.js looks, feels and
- smells a lot like Squeak, I'll take it as a compliment.
+ This Morphic implementation for JavaScript is not a direct port of
+ Squeak's Morphic, but still many individual functions have been
+ ported almost literally from Squeak, sometimes even including their
+ comments, e.g. the morph duplication mechanism fullCopy(). Squeak
+ has been a treasure trove, and if morphic.js looks, feels and
+ smells a lot like Squeak, I'll take it as a compliment.
- Evelyn Eastmond has inspired and encouraged me with her wonderful
- implementation of DesignBlocksJS. Thanks for sharing code, ideas
- and enthusiasm for programming.
+ Evelyn Eastmond has inspired and encouraged me with her wonderful
+ implementation of DesignBlocksJS. Thanks for sharing code, ideas
+ and enthusiasm for programming.
- John Maloney has been my mentor and my source of inspiration for
- these Morphic experiments. Thanks for the critique, the suggestions
- and explanations for all things Morphic and for being my all time
- programming hero.
+ John Maloney has been my mentor and my source of inspiration for
+ these Morphic experiments. Thanks for the critique, the suggestions
+ and explanations for all things Morphic and for being my all time
+ programming hero.
- I have originally written morphic.js in Florian Balmer's Notepad2
- editor for Windows and later switched to Apple's Dashcode. I've also
- come to depend on both Douglas Crockford's JSLint, Mozilla's Firebug
- and Google's Chrome to get it right.
+ I have originally written morphic.js in Florian Balmer's Notepad2
+ editor for Windows and later switched to Apple's Dashcode. I've also
+ come to depend on both Douglas Crockford's JSLint, Mozilla's Firebug
+ and Google's Chrome to get it right.
- IX. contributors
- ----------------------
- Joe Otto found and fixed many early bugs and taught me some tricks.
- Nathan Dinsmore contributed mouse wheel scrolling, cached
- background texture handling and countless bug fixes.
- Ian Reynolds contributed backspace key handling for Chrome.
- Davide Della Casa contributed performance optimizations for Firefox.
+ IX. contributors
+ ----------------------
+ Joe Otto found and fixed many early bugs and taught me some tricks.
+ Nathan Dinsmore contributed mouse wheel scrolling, cached
+ background texture handling and countless bug fixes.
+ Ian Reynolds contributed backspace key handling for Chrome.
+ Davide Della Casa contributed performance optimizations for Firefox.
- - Jens Mönig
+ - Jens Mönig