From f21416527fd7f53b7c7eff6027a5511c4bccfaa7 Mon Sep 17 00:00:00 2001 From: jmoenig Date: Sun, 21 Apr 2013 20:40:24 +0200 Subject: [PATCH] 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. --- gui.js | 13 +- history.txt | 2 + morphic.js | 125 +++- morphic.txt | 1629 ++++++++++++++++++++++++++------------------------- 4 files changed, 944 insertions(+), 825 deletions(-) diff --git a/gui.js b/gui.js index f2ac1ac1..604ab06f 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 f8cb5366..8c39a800 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 0faa43c9..590337a6 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) { @@ -10039,6 +10121,15 @@ WorldMorph.prototype.initEventListeners = function () { false ); + canvas.addEventListener( + "dblclick", + function (event) { + event.preventDefault(); + myself.hand.processDoubleClick(event); + }, + false + ); + canvas.addEventListener( "touchend", function (event) { diff --git a/morphic.txt b/morphic.txt index afa60967..042b7e51 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 . - 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: - - 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: - - - 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 - - * included only for demo purposes - - - III. yet to implement - --------------------- - - keyboard support for scroll frames and lists - - - 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: - - - 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: - - * 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. - - 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"). - - 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. - - 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. - - - (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: - - - - - Morphic! - - - - - -

Your browser doesn't support canvas.

-
- - - - 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;" - - 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: - - example html file: - - - - - Morphic! - - - - -

first world:

- -

Your browser doesn't support canvas.

-
-

second world:

- -

Your browser doesn't support canvas.

-
- - - - - (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: - - - - - touch me! - - - - - -

Your browser doesn't support canvas.

-
- - - - 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 - - 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: - - * hide() - * show() - - * setPosition(aPoint) - * setExtent(aPoint) - * setColor(aColor) - - * add(submorph) - attaches submorph ontop - * addBack(submorph) - attaches submorph underneath + 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: + + 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: + + + 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 + + * included only for demo purposes + + + 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 + + + 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 + + + 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 + + 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. + + + 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. + + 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. + + + (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. + + example html file: + + + + + Morphic! + + + + + +

Your browser doesn't support canvas.

+
+ + + + 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;" + + 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: + + example html file: + + + + + Morphic! + + + + +

first world:

+ +

Your browser doesn't support canvas.

+
+

second world:

+ +

Your browser doesn't support canvas.

+
+ + + + + (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: + + + + + touch me! + + + + + +

Your browser doesn't support canvas.

+
+ + + + 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 + + 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: + + * hide() + * show() + + * setPosition(aPoint) + * setExtent(aPoint) + * setColor(aColor) + + * 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 . 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() - was pressed on a single line of text - cancel() - was pressed on any text element + accept() - was pressed on a single line of text + cancel() - was pressed on any text element - Note that "accept" only gets triggered by single-line texte elements, - as the 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 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