kopia lustrzana https://github.com/backface/turtlestitch
Merge branch 'master' into patch-1
commit
e6d260bf45
File diff suppressed because one or more lines are too long
41
HISTORY.md
41
HISTORY.md
|
@ -1,10 +1,51 @@
|
|||
# Snap! (BYOB) History
|
||||
|
||||
## in development:
|
||||
|
||||
## 6.0.0 beta:
|
||||
* **New Features:**
|
||||
* new Morphic architecture, faster loading, smaller memory footprint, mobile-friendly
|
||||
* hyper-blocks
|
||||
* new "send msg to sprite" primitive in control
|
||||
* new "index of" primitive in lists
|
||||
* new fast "append" reporter in lists
|
||||
* show login status in the cloud button (outline = logged out, solid = logged in)
|
||||
* custom drop-downs (experimental, uses JS)
|
||||
* blockify lists / tables with atomic values in watchers
|
||||
* extended libraries (APL, thanks, Brian) and programmatic handling of variables (thanks, Joan)
|
||||
* "result pic..." context menu entry for reporters (used to be hidden "script pic with result..." option)
|
||||
* more block relabelling options, e.g. for loops
|
||||
* prefix keys in custom drop-down menus with '§_' to only show them if the shift-key is pressed
|
||||
* new "id" option in the monadic function reporter primitive (hyperizable to support deep copies of nested lists)
|
||||
* **Notable Changes:**
|
||||
* repeated WARPs inside loops have been sped up
|
||||
* duplicated blocks / scripts are grabbed by their top-left corner rather than their center
|
||||
* close all widgets when opening a new project
|
||||
* scan first ten rows of a list to determine the number of columns to show in table views
|
||||
* give duplicated custom block definitions unique names
|
||||
* sort sound and message names in drop-down menus alphabetically
|
||||
* changed result for FIND to empty instead of false if none is found
|
||||
* new flat design
|
||||
* increased contrast in dark mode
|
||||
* toggling Retina support has been hidden (because it no longer works the same)
|
||||
* **Notable Fixes:**
|
||||
* multi-c slots embedding reporters has been disabled
|
||||
* programmatically changing a clone from "permanent" to "temporary" now works in presentation mode
|
||||
* costumes and sounds of clones are now properly shadowed when modifying them programmatically
|
||||
* fixed editing cells in multi-page list watchers
|
||||
* recursive calls to "broadcast and wait" execute smoothly again
|
||||
* expanding a collapsed comment or clicking on it now brings it to the front
|
||||
* long project titles no longer overlap other buttons in the control bar
|
||||
* "empty" continuations referring to the end of a script no longer throw an error.
|
||||
* **Translation Updates:**
|
||||
* New Hebrew translation
|
||||
* Ukranian
|
||||
* Catalan
|
||||
* Portuguese
|
||||
* Chinese
|
||||
* Japanese
|
||||
* Bengali
|
||||
* German
|
||||
|
||||
## 5.4.5:
|
||||
* **Notable Change:**
|
||||
|
|
Plik binarny nie jest wyświetlany.
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
if (window.location) {
|
||||
window.location = 'snap.html' + window.location.hash;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,12 +1,15 @@
|
|||
iteration-composition.xml Iteration, composition Traditional loop constructs (while, until, etc.) plus the Lisp "named let" (a generalization of FOR) plus functional iteration (repeated invocation of a function) and function composition.
|
||||
list-utilities.xml List utilities Some standard functions on lists (append, reverse, etc.)
|
||||
apl.xml APL primitives Adds features from the APL language supporting hyperblocks.
|
||||
stream-tools.xml Streams (lazy lists) A variation on the list data type in which each list item aren't computed until it's needed, so you can construct million-item lists without really taking up all that time or memory, or even infinite-sized lists. (A block that reports all the prime numbers is included as an example.)
|
||||
variadic-reporters.xml Variadic reporters Versions of +, x, AND, and OR that take more than two inputs.
|
||||
httpBlocks.xml Web services access (https) An extended version of the HTTP:// block that allows POST, PUT, and DELETE as well as GET requests, allows using the secure HTTPS protocol, and gives control over headers, etc.
|
||||
word-sentence.xml Words, sentences One of the big ideas in Logo that they left out of Scratch is thinking of text as structured into words and sentences, rather than just a string of characters. This library brings back that idea.
|
||||
bar-charts.xml Bar charts from tables Takes a table (typically from a CSV data set) as input and reports a summary of the table grouped by the field in the specified column number. The remaining three inputs are used only if the field values are numbers, in which case they can be grouped into buckets (e.g., decades, centuries, etc.). Those three inputs specify the smallest and largest values of interest and, most importantly, the width of a bucket (10 for decades, 100 for centuries). If the field isn't numeric, leave these three inputs empty or set them to zero. In that case, each string value of the field is its own bucket, and they appear sorted alphabetically. The block reports a new table with three columns. The first column contains the bucket name or smallest number. The second column contains a nonnegative integer that says how many records in the input table fall into this bucket. The third column is a subtable containing the actual records from the original table that fall into the bucket. If your buckets aren't of constant width, or you want to group by some function of more than one field, load the "Frequency Distribution Analysis" library instead.
|
||||
cases.xml Multi-branched conditional (switch) Like "switch" in C-like languages or "cond" in Lisp. Thanks to Nathan Dinsmore for inventing the idea of a separate block for each branch!
|
||||
leap-library.xml LEAP Motion controller Report hand positions from LEAP Motion controller (leapmotion.com).
|
||||
textCostumes_module.xml Text Costumes Generate costumes from letters or words of text.
|
||||
colors.xml Crayons, fair hues, color done right Incorporates the crayon library and the set RGB library. Implements fair hues (more orange, less green, adds brown) and a linear color scale including grayscale and fair-hue-based shades.
|
||||
setrgb.xml Set RGB or HSV pen color Set or report pen color as RGB (red, green, blue) or HSV (hue, saturation, value).
|
||||
try-catch.xml Catch errors in a script Run a script; if an error happens, instead of stopping the script with a red halo, run another script to handle the error. Also includes a block to cause an error with a message given as input. Also includes a block to create a script variable and give it a value.
|
||||
multiline.xml Allow multi-line text input to a block In general, text inputs allow only a single line. The MULTILINE block accepts multi-line text input and can be used in text input slots of other blocks.
|
||||
|
@ -20,7 +23,9 @@ audioComp_module.xml Audio Comp analyze, manipulate and generate sound samples.
|
|||
atomic_HOFs_module.xml "Bigger" Data [EXPERIMENTAL] crunch large lists very fast
|
||||
frequency_distribution_module.xml Frequency Distribution Analysis [EXPERIMENTAL] analyze data for frequency distribution
|
||||
maps_module.xml World Map [EXPERIMENTAL] add interactive maps to projects
|
||||
make-variables.xml create variables in program declare global or sprite-local variables in a script
|
||||
make-variables.xml Create variables in program Create and manage global/sprite/script variables in a script
|
||||
json.xml Deal with JSON data Turn JSON strings into lists with the listify block, then retrieve data out of them by using the value at key block.
|
||||
parallel_module.xml Parallelization Run several scripts in parallel and wait until all are done.
|
||||
strings.xml String processing Extract substrings of a string in various ways
|
||||
HummingbirdBlocks.xml Hummingbird robotics Control the Hummingbird robotics kit processor
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
<blocks app="Snap! 5.0, http://snap.berkeley.edu" version="1"><block-definition s="show picture %'bitmap'" type="command" category="looks"><comment w="88" collapsed="false">display the given bitmap in a copy of the current costume (so you can switch back to the original costume again)</comment><header></header><code></code><translations>pt:mostra a imagem _
</translations><inputs><input type="%l"></input></inputs><script><block s="doSwitchToCostume"><block s="reportNewCostumeStretched"><block var="bitmap"/><block s="reportGetImageAttribute"><l><option>width</option></l><l><option>current</option></l></block><block s="reportGetImageAttribute"><l><option>height</option></l><l><option>current</option></l></block></block></block></script></block-definition><block-definition s="$camera snap" type="reporter" category="sensing"><comment x="0" y="0" w="216" collapsed="false">takes a snapshot with the webcam and reports it as a new costume, or zero if the user cancels</comment><header></header><code></code><translations>pt:$camera a imagem actual do vídeo
</translations><inputs></inputs><script><block s="doDeclareVariables"><list><l>test</l><l>pic</l></list></block><block s="doSetVar"><l>test</l><block s="evaluate"><block s="reportJSFunction"><list></list><l>var camDialog,
 result = false;

camDialog = new CamSnapshotDialogMorph(
 this.parentThatIsA(IDE_Morph),
 this,
 function () {result = null; },
 function (costume) {
 result = costume;
 this.close();
 }
);

camDialog.key = 'camera';
camDialog.popUp(this.world());
return function () {return result; };</l></block><list></list></block></block><block s="doWaitUntil"><block s="evaluate"><block s="reifyScript"><script><block s="doSetVar"><l>pic</l><block s="evaluate"><block var="test"/><list></list></block></block><block s="doReport"><block s="reportNot"><block s="reportEquals"><block var="pic"/><block s="reportBoolean"><l><bool>false</bool></l></block></block></block></block></script><list></list></block><list></list></block></block><block s="doReport"><block var="pic"/></block></script></block-definition></blocks>
|
||||
<blocks app="Snap! 6.0 beta, https://snap.berkeley.edu" version="1"><block-definition s="$camera snap" type="reporter" category="sensing"><comment x="0" y="0" w="216" collapsed="false">takes a snapshot with the webcam and reports it as a new costume, or zero if the user cancels</comment><header></header><code></code><translations>pt:$camera a imagem actual do vídeo
</translations><inputs></inputs><script><block s="doDeclareVariables"><list><l>test</l><l>pic</l></list></block><block s="doSetVar"><l>test</l><block s="evaluate"><block s="reportJSFunction"><list></list><l>var camDialog,
 result = false;

camDialog = new CamSnapshotDialogMorph(
 this.parentThatIsA(IDE_Morph),
 this,
 function () {result = null; },
 function (costume) {
 result = costume;
 this.close();
 }
);

camDialog.key = 'camera';
camDialog.popUp(this.world());
return function () {return result; };</l></block><list></list></block></block><block s="doWaitUntil"><block s="evaluate"><block s="reifyScript"><script><block s="doSetVar"><l>pic</l><block s="evaluate"><block var="test"/><list></list></block></block><block s="doReport"><block s="reportNot"><block s="reportEquals"><block var="pic"/><block s="reportBoolean"><l><bool>false</bool></l></block></block></block></block></script><list></list></block><list></list></block></block><block s="doReport"><block var="pic"/></block></script></block-definition></blocks>
|
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
<blocks app="Snap! 5.0, http://snap.berkeley.edu" version="1"><block-definition s="costume from text %'text' size %'size'" type="reporter" category="looks"><header></header><code></code><translations>de:Kostüm aus Text _ Größe _
pt:um traje com o texto _ de tamanho _
</translations><inputs><input type="%s">A</input><input type="%n">72</input></inputs><script><block s="doDeclareVariables"><list><l>costume</l><l>x</l><l>y</l><l>dir</l><l>cst</l><l>trails</l></list></block><block s="doSetVar"><l>x</l><block s="xPosition"></block></block><block s="doSetVar"><l>y</l><block s="yPosition"></block></block><block s="doSetVar"><l>dir</l><block s="direction"></block></block><block s="doSetVar"><l>cst</l><block s="getCostumeIdx"></block></block><block s="up"></block><block s="gotoXY"><l>0</l><l>0</l></block><block s="setHeading"><l>90</l></block><block s="doSwitchToCostume"><l><option>Turtle</option></l></block><block s="doSetVar"><l>trails</l><block s="reportPenTrailsAsCostume"></block></block><block s="clear"></block><block s="write"><block var="text"/><block var="size"/></block><block s="doSetVar"><l>costume</l><block s="reportPenTrailsAsCostume"></block></block><block s="clear"></block><block s="doRun"><block s="reportJSFunction"><list><l>costume</l><l>name</l></list><l>costume.name = name;</l></block><list><block var="costume"/><block var="text"/></list></block><block s="gotoXY"><l>0</l><l>0</l></block><block s="doSwitchToCostume"><block var="trails"/></block><block s="doStamp"></block><block s="doSwitchToCostume"><block var="cst"/></block><block s="gotoXY"><block var="x"/><block var="y"/></block><block s="setHeading"><block var="dir"/></block><block s="doReport"><block var="costume"/></block></script></block-definition></blocks>
|
||||
<blocks app="Snap! 6.0 beta, https://snap.berkeley.edu" version="1"><block-definition s="costume from text %'text' size %'size'" type="reporter" category="looks"><header></header><code></code><translations>de:Kostüm aus Text _ Größe _
pt:um traje com o texto _ de tamanho _
</translations><inputs><input type="%s">A</input><input type="%n">72</input></inputs><script><block s="doDeclareVariables"><list><l>costume</l><l>x</l><l>y</l><l>dir</l><l>cst</l><l>trails</l></list></block><block s="doSetVar"><l>x</l><block s="xPosition"></block></block><block s="doSetVar"><l>y</l><block s="yPosition"></block></block><block s="doSetVar"><l>dir</l><block s="direction"></block></block><block s="doSetVar"><l>cst</l><block s="getCostumeIdx"></block></block><block s="up"></block><block s="gotoXY"><l>0</l><l>0</l></block><block s="setHeading"><l>90</l></block><block s="doSwitchToCostume"><l><option>Turtle</option></l></block><block s="doSetVar"><l>trails</l><block s="reportPenTrailsAsCostume"></block></block><block s="clear"></block><block s="gotoXY"><block s="reportAttributeOf"><l><option>left</option></l><l>Stage</l></block><l>0</l></block><block s="write"><block var="text"/><block var="size"/></block><block s="gotoXY"><block s="reportAttributeOf"><l><option>left</option></l><l>Stage</l></block><l>0</l></block><block s="doSetVar"><l>costume</l><block s="reportPenTrailsAsCostume"></block></block><block s="clear"></block><block s="doRun"><block s="reportJSFunction"><list><l>costume</l><l>name</l></list><l>costume.name = name;</l></block><list><block var="costume"/><block var="text"/></list></block><block s="gotoXY"><l>0</l><l>0</l></block><block s="doSwitchToCostume"><block var="trails"/></block><block s="doStamp"></block><block s="doSwitchToCostume"><block var="cst"/></block><block s="gotoXY"><block var="x"/><block var="y"/></block><block s="setHeading"><block var="dir"/></block><block s="doReport"><block var="costume"/></block></script></block-definition></blocks>
|
File diff suppressed because one or more lines are too long
|
@ -184,7 +184,7 @@ SnapTranslator.dict.bn = {
|
|||
'translator_e-mail':
|
||||
'mokter@gmail.com', // optional
|
||||
'last_changed':
|
||||
'2014-07-02', // this, too, will appear in the Translators tab
|
||||
'2020-06-25', // this, too, will appear in the Translators tab
|
||||
|
||||
|
||||
// GUI
|
||||
|
@ -196,23 +196,23 @@ SnapTranslator.dict.bn = {
|
|||
|
||||
// categories:
|
||||
'Motion':
|
||||
'গতি (Motion)',
|
||||
'গতি',
|
||||
'Looks':
|
||||
'সৌন্দর্য (Looks)',
|
||||
'সৌন্দর্য',
|
||||
'Sound':
|
||||
'শব্দ (Sound)',
|
||||
'শব্দ',
|
||||
'Pen':
|
||||
'লেখনী (Pen)',
|
||||
'লেখনী',
|
||||
'Control':
|
||||
'নিয়ন্ত্রণ (Control)',
|
||||
'নিয়ন্ত্রণ',
|
||||
'Sensing':
|
||||
'অনুভূতি (Sensing)',
|
||||
'অনুভূতি',
|
||||
'Operators':
|
||||
'চালক (Operators)',
|
||||
'চালক',
|
||||
'Variables':
|
||||
'চলক (Variables)',
|
||||
'চলক',
|
||||
'Lists':
|
||||
'তালিকা (Lists)',
|
||||
'তালিকা',
|
||||
'Other':
|
||||
'অন্যান্য',
|
||||
|
||||
|
@ -222,11 +222,11 @@ SnapTranslator.dict.bn = {
|
|||
|
||||
// tabs:
|
||||
'Scripts':
|
||||
'প্রোগ্রাম স্ক্রিপ্ট (Scripts)',
|
||||
'প্রোগ্রাম স্ক্রিপ্ট',
|
||||
'Costumes':
|
||||
'পরিচ্ছদ (Costumes)',
|
||||
'পরিচ্ছদ',
|
||||
'Sounds':
|
||||
'শব্দমালা (Sounds)',
|
||||
'শব্দমালা',
|
||||
|
||||
// names:
|
||||
'Sprite':
|
||||
|
|
|
@ -483,6 +483,8 @@ SnapTranslator.dict.ca = {
|
|||
'Envia a tots %msg',
|
||||
'broadcast %msg and wait':
|
||||
'Envia a tots %msg i espera',
|
||||
'send %msg to %spr':
|
||||
'envia %msg a %spr',
|
||||
'Message name':
|
||||
'Nom del missatge',
|
||||
'message':
|
||||
|
@ -708,6 +710,8 @@ SnapTranslator.dict.ca = {
|
|||
'cosa',
|
||||
'is %l empty?':
|
||||
'%l buida?',
|
||||
'index of %s in %l':
|
||||
'índex de %s a %l',
|
||||
'map %repRing over %l':
|
||||
'mapeja %repRing sobre %l',
|
||||
'keep items %predRing from %l':
|
||||
|
@ -732,6 +736,8 @@ SnapTranslator.dict.ca = {
|
|||
'per cada %upvar de %l %cla',
|
||||
'item':
|
||||
'element',
|
||||
'append %lists':
|
||||
'annexa %lists',
|
||||
'add %s to %l':
|
||||
'afegeix %s a %l',
|
||||
'delete %ida of %l':
|
||||
|
@ -794,8 +800,8 @@ SnapTranslator.dict.ca = {
|
|||
'mostra tot el projecte en format XML\nen una altra finestra del navegador',
|
||||
'Export blocks...':
|
||||
'Exporta els blocs...',
|
||||
'show global custom block definitions as XML\nin a new browser window':
|
||||
'exporta els blocs personalitzats que triis\nen un arxiu en format XML',
|
||||
'save global custom block\ndefinitions as XML':
|
||||
'desa els blocs personalitzats globals\nen format XML',
|
||||
'Unused blocks...':
|
||||
'Blocs no utilitzats...',
|
||||
'find unused global custom blocks\nand remove their definitions':
|
||||
|
@ -808,8 +814,8 @@ SnapTranslator.dict.ca = {
|
|||
'bloc(s) personalitzats no utilitzats esborrats',
|
||||
'Export summary...':
|
||||
'Exporta el resum...',
|
||||
'open a new browser browser window\n with a summary of this project':
|
||||
'Obre una finestra del navegador\namb un resum d\'aquest projecte',
|
||||
'save a summary\nof this project':
|
||||
'Desa un resum\nd\'aquest projecte',
|
||||
|
||||
'Contents':
|
||||
'Continguts',
|
||||
|
@ -999,8 +1005,8 @@ SnapTranslator.dict.ca = {
|
|||
'esborra\'m',
|
||||
'script pic...':
|
||||
'mostra la meva imatge...',
|
||||
'open a new window\nwith a picture of this script':
|
||||
'obre una nova finestra\namb una imatge d\'aquest programa',
|
||||
'save a picture\nof this script':
|
||||
'desa una imatge d\'aquest programa',
|
||||
'ringify':
|
||||
'encapsula\'m',
|
||||
'unringify':
|
||||
|
@ -1063,8 +1069,8 @@ SnapTranslator.dict.ca = {
|
|||
'mostra\'ls tots',
|
||||
'pic...':
|
||||
'exporta com a imatge...',
|
||||
'open a new window\nwith a picture of the stage':
|
||||
'obre una nova finestra\namb una foto de l\'escenari',
|
||||
'save a picture\nof the stage':
|
||||
'desa una imatge\nde l\'escenari',
|
||||
|
||||
// scripting area
|
||||
'clean up':
|
||||
|
@ -1083,8 +1089,8 @@ SnapTranslator.dict.ca = {
|
|||
'utilitza el teclat\nper escriure els blocs',
|
||||
'scripts pic...':
|
||||
'exporta com a imatge...',
|
||||
'open a new window\nwith a picture of all scripts':
|
||||
'obre una nova finestra\namb la imatge d\'aquests programes',
|
||||
'save a picture\nof all scripts':
|
||||
'desa una imatge de tots els blocs de programes',
|
||||
'make a block...':
|
||||
'crea un bloc...',
|
||||
|
||||
|
@ -1164,8 +1170,8 @@ SnapTranslator.dict.ca = {
|
|||
// Project Manager
|
||||
'Untitled':
|
||||
'Sense títol',
|
||||
'Open un Project':
|
||||
'Obre projecte',
|
||||
'Open Project':
|
||||
'Obre un projecte',
|
||||
'(empty)':
|
||||
'(buit)',
|
||||
'Saved!':
|
||||
|
@ -1831,8 +1837,8 @@ SnapTranslator.dict.ca = {
|
|||
'marqueu per transformar els\nSVG a mapa de bits en importar',
|
||||
'comment pic...':
|
||||
'imatge del comentari…',
|
||||
'open a new window\nwith a picture of this comment':
|
||||
'obre una finestra\namb una imatge del comentari',
|
||||
'save a picture\nof this comment':
|
||||
'desa una imatge\ndel comentari',
|
||||
'undo':
|
||||
'desfés',
|
||||
//Paint editors
|
||||
|
@ -2084,8 +2090,6 @@ SnapTranslator.dict.ca = {
|
|||
'Substitueix el Projecte',
|
||||
'Are you sure you want to replace':
|
||||
'Esteu segur que voleu substituir el projecte original',
|
||||
'Open Project':
|
||||
'Obre el Projecte',
|
||||
'password has been changed.':
|
||||
's\'ha canviat la contrasenya.',
|
||||
'SVG costumes are\nnot yet fully supported\nin every browser':
|
||||
|
@ -2094,8 +2098,10 @@ SnapTranslator.dict.ca = {
|
|||
'Desa el Projecte',
|
||||
'script pic with result...':
|
||||
'imatge del programa i del resultat…',
|
||||
'open a new window\nwith a picture of both\nthis script and its result':
|
||||
'obre una finestra\n amb el programa i el resultat',
|
||||
'result pic...':
|
||||
'imatge del resultat...',
|
||||
'save a picture of both\nthis script and its result':
|
||||
'desa una imatge\ndels blocs amb el resultat',
|
||||
'JavaScript function ( %mult%s ) { %code }':
|
||||
'JavaScript function ( %mult%s ) { %code }',
|
||||
'Select categories of additional blocks to add to this project.':
|
||||
|
@ -2150,8 +2156,8 @@ SnapTranslator.dict.ca = {
|
|||
'URL…',
|
||||
'Export summary with drop-shadows...':
|
||||
'Exporta el resum amb les imatges ombrejades…',
|
||||
'open a new browser browser window\nwith a summary of this project\nwith drop-shadows on all pictures.\nnot supported by all browsers':
|
||||
'Obre una finestra del navegador\namb un resum del projecte i\namb totes les imatges ombrejades.\n No tots els navegadors suporten aquesta funcionalitat',
|
||||
'download and save\nwith a summary of this project\nwith drop-shadows on all pictures.\nnot supported by all browsers':
|
||||
'Desa i descarrega\nun resum del projecte\namb totes les imatges ombrejades.\n No tots els navegadors suporten aquesta funcionalitat',
|
||||
'specify the distance the hand has to move\nbefore it picks up an object':
|
||||
'especifica a què distància\ns\'han d\'arrossegar els blocs\nper a que es moguin',
|
||||
'block variables...':
|
||||
|
@ -2608,7 +2614,7 @@ SnapTranslator.dict.ca = {
|
|||
'Anàlisi de la distribució de freqüències',
|
||||
'World Map':
|
||||
'Mapa del món',
|
||||
'create variables in program':
|
||||
'Create variables in program':
|
||||
'Creant variables des del programa',
|
||||
'Deal with JSON data':
|
||||
'Tractament de dades JSON',
|
||||
|
@ -2662,8 +2668,8 @@ SnapTranslator.dict.ca = {
|
|||
'[EXPERIMENTAL] Analitza dades per fer freqüències de distribució',
|
||||
'[EXPERIMENTAL] add interactive maps to projects':
|
||||
'[EXPERIMENTAL] Afegeix mapes cartogràfics interactius als teus projectes',
|
||||
'declare global or sprite-local variables in a script':
|
||||
'Declara i assigna variables (locals o globals) dins els programes',
|
||||
'Create and manage global/sprite/script variables in a script':
|
||||
'Crea i gestiona variables de tipus global/sprite/script dins els programes',
|
||||
'Turn JSON strings into lists with the listify block, then retrieve data out of them by using the value at key block.':
|
||||
'Converteix les cadenes JSON en llistes utilitzant el bloc "listify". Utilitza els blocs de valors i claus per gestionar aquest tipus de llistes',
|
||||
'Run several scripts in parallel and wait until all are done.':
|
||||
|
@ -2740,6 +2746,14 @@ SnapTranslator.dict.ca = {
|
|||
'svg...':
|
||||
'exporta vectors com a svg',
|
||||
'export pen trails\nline segments as SVG':
|
||||
'exporta els vectors dibuixats com a fitxer SVG'
|
||||
'exporta els vectors dibuixats com a fitxer SVG',
|
||||
'blockify':
|
||||
'en forma de blocs',
|
||||
'Hyper blocks support':
|
||||
'Suport a hiperblocs',
|
||||
'uncheck to disable\nusing operators on lists and tables':
|
||||
'desmarqueu per deshabilitar\nla utilització dels operadors\nsobre llistes i taules',
|
||||
'check to enable\nusing operators on lists and tables':
|
||||
'marqueu per habilitar\nla utilització dels operadors\nsobre llistes i taules'
|
||||
|
||||
};
|
||||
|
|
|
@ -185,7 +185,7 @@ SnapTranslator.dict.de = {
|
|||
'translator_e-mail':
|
||||
'jens@moenig.org, jadga.huegle@sap.com', // optional
|
||||
'last_changed':
|
||||
'2020-01-28', // this, too, will appear in the Translators tab
|
||||
'2020-06-08', // this, too, will appear in the Translators tab
|
||||
|
||||
// GUI
|
||||
// control bar:
|
||||
|
@ -525,6 +525,8 @@ SnapTranslator.dict.de = {
|
|||
'sende %msg an alle',
|
||||
'broadcast %msg and wait':
|
||||
'sende %msg an alle und warte',
|
||||
'send %msg to %spr':
|
||||
'sende %msg an %spr',
|
||||
'Message name':
|
||||
'Nachricht',
|
||||
'message':
|
||||
|
@ -799,6 +801,8 @@ SnapTranslator.dict.de = {
|
|||
'etwas',
|
||||
'is %l empty?':
|
||||
'ist %l leer?',
|
||||
'index of %s in %l':
|
||||
'Index von %s in %l',
|
||||
'map %repRing over %l':
|
||||
'wende %repRing an auf %l',
|
||||
'keep items %predRing from %l':
|
||||
|
@ -823,6 +827,8 @@ SnapTranslator.dict.de = {
|
|||
'Wert',
|
||||
'index':
|
||||
'Index',
|
||||
'append %lists':
|
||||
'verbinde %lists',
|
||||
'add %s to %l':
|
||||
'f\u00fcge %s zu %l hinzu',
|
||||
'delete %ida of %l':
|
||||
|
@ -887,8 +893,8 @@ SnapTranslator.dict.de = {
|
|||
'zeigt das Projekt als XML\nin einem neuen Browserfenster an',
|
||||
'Export blocks...':
|
||||
'Bl\u00f6cke exportieren...',
|
||||
'show global custom block definitions as XML\nin a new browser window':
|
||||
'zeigt globale Benutzerblockdefinitionen\nals XML im Browser an',
|
||||
'save global custom block\ndefinitions as XML':
|
||||
'globale Benutzerblockdefinitionen\nals XML-Datei speichern',
|
||||
'Unused blocks...':
|
||||
'nicht verwendete Bl\u00f6cke...',
|
||||
'find unused global custom blocks\nand remove their definitions':
|
||||
|
@ -901,9 +907,8 @@ SnapTranslator.dict.de = {
|
|||
'nicht verwendete Bl\u00f6cke entfernt',
|
||||
'Export summary...':
|
||||
'Zusammenfassung exportieren...',
|
||||
'open a new browser browser window\n with a summary of this project':
|
||||
'eine Zusammenfassung dieses Projekts\nin einem neuen Browserfenster'
|
||||
+ 'anzeigen',
|
||||
'save a summary\nof this project':
|
||||
'eine Zusammenfassung\ndieses Projekts speichern',
|
||||
'Contents':
|
||||
'Inhalt',
|
||||
'Kind of':
|
||||
|
@ -960,7 +965,7 @@ SnapTranslator.dict.de = {
|
|||
'GUI Elemente programmatisch bearbeiten',
|
||||
'Allow multi-line text input to a block':
|
||||
'Mehrzeiliger Text als Eingabe für Blöcke',
|
||||
'create variables in program':
|
||||
'Create variables in program':
|
||||
'Variablen im Skript erstellen',
|
||||
|
||||
// cloud menu
|
||||
|
@ -1114,6 +1119,12 @@ SnapTranslator.dict.de = {
|
|||
'Ternäre Bool\'sche Inputs',
|
||||
'Inheritance support':
|
||||
'Prototypische Vererbung',
|
||||
'Hyper blocks support':
|
||||
'Hyper-Blöcke',
|
||||
'uncheck to disable\nusing operators on lists and tables':
|
||||
'erweiterte Anwendung von Operatoren\nauf Listen und Tabellen',
|
||||
'check to enable\nusing operators on lists and tables':
|
||||
'erweiterte Anwendung von Operatoren\nauf Listen und Tabellen',
|
||||
'Log pen vectors':
|
||||
'Malstiftvektoren aufzeichnen',
|
||||
'uncheck to turn off\nlogging pen vectors':
|
||||
|
@ -1163,8 +1174,12 @@ SnapTranslator.dict.de = {
|
|||
'L\u00f6schen',
|
||||
'script pic...':
|
||||
'Skriptbild...',
|
||||
'open a new window\nwith a picture of this script':
|
||||
'ein neues Browserfenster mit einem\nBild dieses Skripts \u00f6ffnen',
|
||||
'save a picture\nof this script':
|
||||
'ein Bild dieses\nSkripts speichern',
|
||||
'result pic...':
|
||||
'Ergebnisbild...',
|
||||
'save a picture of both\nthis script and its result':
|
||||
'ein Bild dieses Skripts mit\nseinem Ergebnis speichern',
|
||||
'ringify':
|
||||
'Umringen',
|
||||
'unringify':
|
||||
|
@ -1221,8 +1236,8 @@ SnapTranslator.dict.de = {
|
|||
'Alles zeigen',
|
||||
'pic...':
|
||||
'Bild exportieren...',
|
||||
'open a new window\nwith a picture of the stage':
|
||||
'ein neues Browserfenster mit einem\nBild der B\u00fchne \u00f6ffnen',
|
||||
'save a picture\nof the stage':
|
||||
'ein Bild der\nBühne speichern',
|
||||
'svg...':
|
||||
'SVG exportieren...',
|
||||
'export pen trails\nline segments as SVG':
|
||||
|
@ -1251,8 +1266,8 @@ SnapTranslator.dict.de = {
|
|||
'Blöcke per Tastatur\neingeben',
|
||||
'scripts pic...':
|
||||
'Bild aller Skripte...',
|
||||
'open a new window\nwith a picture of all scripts':
|
||||
'ein neues Browserfenster mit einem\nBild aller Skripte \u00f6ffnen',
|
||||
'save a picture\nof all scripts':
|
||||
'ein Bild aller\nSkripte speichern',
|
||||
'make a block...':
|
||||
'Neuen Block bauen...',
|
||||
|
||||
|
@ -1285,6 +1300,8 @@ SnapTranslator.dict.de = {
|
|||
'Tabelle',
|
||||
'open in dialog...':
|
||||
'in neuem Fenster \u00f6ffnen',
|
||||
'blockify':
|
||||
'als Block',
|
||||
'reset columns':
|
||||
'Spaltenbreiten zur\u00fccksetzen',
|
||||
'items':
|
||||
|
@ -1604,8 +1621,8 @@ SnapTranslator.dict.de = {
|
|||
'Anmerkung hier hinzuf\u00fcgen',
|
||||
'comment pic...':
|
||||
'Kommentarbild',
|
||||
'open a new window\nwith a picture of this comment':
|
||||
'neues Fenster mit dem Bild\ndieses Kommentars öffnen',
|
||||
'save a picture\nof this comment':
|
||||
'ein Bild dieser\nAnmerkung speichern',
|
||||
|
||||
// drow downs
|
||||
// directions
|
||||
|
|
|
@ -2577,7 +2577,7 @@ SnapTranslator.dict.gl = {
|
|||
'Analise da distribución de frecuencias',
|
||||
'World Map':
|
||||
'Mapa do mundo',
|
||||
'create variables in program':
|
||||
'Create variables in program':
|
||||
'Creando variábeis dende o programa',
|
||||
'Deal with JSON data':
|
||||
'Tratar con datos JSON',
|
||||
|
@ -2629,8 +2629,8 @@ SnapTranslator.dict.gl = {
|
|||
'[EXPERIMENTAL] Procesar listas grandes de forma moi rápida.',
|
||||
'[EXPERIMENTAL] analyze data for frequency distribution':
|
||||
'[EXPERIMENTAL] Analizar datos para obter a súa distribución de frecuencias.',
|
||||
'declare global or sprite-local variables in a script':
|
||||
'Declarar variábeis globais ou de obxecto a partires dun programa.',
|
||||
'Create and manage global/sprite/script variables in a script':
|
||||
'Crear e xestionar "global/sprite/script" variábeis a partires dun programa.',
|
||||
'Turn JSON strings into lists with the listify block, then retrieve data out of them by using the value at key block.':
|
||||
'Transforma as cadeas JSON en listas co bloque «listify» (listar), e após recupera datos delas usando o valor do bloque de chaves.',
|
||||
'Run several scripts in parallel and wait until all are done.':
|
||||
|
|
|
@ -963,7 +963,7 @@ SnapTranslator.dict.he = {
|
|||
' הגדרת מאפייני ממשק ',
|
||||
'Allow multi-line text input to a block':
|
||||
'אפשר קליטת משפטים מרוביי שורות',
|
||||
'create variables in program':
|
||||
'Create variables in program':
|
||||
'צור משתנים בתוכנית',
|
||||
|
||||
// cloud menu
|
||||
|
|
|
@ -590,11 +590,11 @@ SnapTranslator.dict.ja = {
|
|||
'add %s to %l':
|
||||
'%s を %l に追加する',
|
||||
'delete %ida of %l':
|
||||
'%ida を %l から削除する',
|
||||
'%ida 番目を %l から削除する',
|
||||
'insert %s at %idx of %l':
|
||||
'%s を %idx 番目に挿入する %l',
|
||||
'%s を %idx 番目になるように %l に挿入する ',
|
||||
'replace item %idx of %l with %s':
|
||||
'%idx 番目 %l を %s で置き換える',
|
||||
'%idx 番目の %l の要素を %s で置き換える',
|
||||
|
||||
// other
|
||||
'Make a block':
|
||||
|
|
|
@ -960,7 +960,7 @@ SnapTranslator.dict.pt = {
|
|||
'Repórteres para obter e comandos para alterar todas as configurações globais',
|
||||
'Allow multi-line text input to a block':
|
||||
'Permitir texto com múltiplas linhas como entrada',
|
||||
'create variables in program':
|
||||
'Create variables in program':
|
||||
'Criar variáveis não locais (globais ou de objecto) num guião',
|
||||
|
||||
// menu da nuvem
|
||||
|
@ -2452,8 +2452,8 @@ SnapTranslator.dict.pt = {
|
|||
'Mapa do mundo',
|
||||
'[EXPERIMENTAL] add interactive maps to projects':
|
||||
'[EXPERIMENTAL] Adicionar mapas interactivos a projectos.',
|
||||
'declare global or sprite-local variables in a script':
|
||||
'Criar variáveis globais ou de objecto a partir de um guião.',
|
||||
'Create and manage global/sprite/script variables in a script':
|
||||
'Criar e gerir variáveis globais, de objecto e de guião a partir de um guião.',
|
||||
'Deal with JSON data':
|
||||
'Lidar com dados JSON',
|
||||
'Turn JSON strings into lists with the listify block, then retrieve data out of them by using the value at key block.':
|
||||
|
|
|
@ -959,8 +959,8 @@ SnapTranslator.dict.sk = {
|
|||
'Programov\u00E9 spracovanie GUI elementov',
|
||||
'Allow multi-line text input to a block':
|
||||
'Povoli\u0165 viac riadkov\u00FD text pre blok',
|
||||
'create variables in program':
|
||||
'vytvori\u0165 premenn\u00E9 v programe',
|
||||
'Create variables in program':
|
||||
'Vytvori\u0165 premenn\u00E9 v programe',
|
||||
|
||||
// cloud menu
|
||||
'Login...':
|
||||
|
|
|
@ -950,7 +950,7 @@ SnapTranslator.dict.tr = {
|
|||
'GUI kontrollü tüm global ayarlar için alıcılar ve ayarlayıcılar sağlayın',
|
||||
'Allow multi-line text input to a block':
|
||||
'Bloklara çok satırlı metin girişi sağla',
|
||||
'create variables in program':
|
||||
'Create variables in program':
|
||||
'Betiklerde değişken yarat',
|
||||
// cloud menu
|
||||
'Login...':
|
||||
|
|
|
@ -165,6 +165,8 @@ SnapTranslator.dict.zh_CN = {
|
|||
'脚本',
|
||||
'Costumes':
|
||||
'造型',
|
||||
'Backgrounds':
|
||||
'背景',
|
||||
'Sounds':
|
||||
'声音',
|
||||
|
||||
|
@ -187,6 +189,10 @@ SnapTranslator.dict.zh_CN = {
|
|||
'添加角色',
|
||||
'add a new Turtle sprite':
|
||||
'添加一个海龟角色',
|
||||
'paint a new sprite':
|
||||
'绘制一个新角色',
|
||||
'take a camera snapshot and\nimport it as a new sprite':
|
||||
'用摄像头拍摄一个新角色',
|
||||
|
||||
// tab help
|
||||
'costumes tab help':
|
||||
|
@ -262,8 +268,8 @@ SnapTranslator.dict.zh_CN = {
|
|||
'下一个造型',
|
||||
'costume #':
|
||||
'造型编号',
|
||||
'costume name':
|
||||
'造型名称',
|
||||
'new costume %l width %dim height %dim':
|
||||
'创建造型 %l 宽度 %dim 高度 %dim',
|
||||
'say %s for %n secs':
|
||||
'说 %s %n 秒',
|
||||
'say %s':
|
||||
|
@ -582,6 +588,8 @@ SnapTranslator.dict.zh_CN = {
|
|||
'长度',
|
||||
'number of channels':
|
||||
'通道数',
|
||||
'new sound %l rate %rate Hz':
|
||||
'创建声音 %l 频率 %rate Hz',
|
||||
'sample rate':
|
||||
'样本频率',
|
||||
'samples':
|
||||
|
@ -609,7 +617,9 @@ SnapTranslator.dict.zh_CN = {
|
|||
'play frequency %n Hz':
|
||||
'演奏频率 %n 赫兹',
|
||||
'stop frequency':
|
||||
'停止演奏频率',
|
||||
'停止演奏频率',
|
||||
'play %n Hz for %n secs':
|
||||
'演奏频率 %n 赫兹 %n 秒',
|
||||
|
||||
// pen:
|
||||
'clear':
|
||||
|
@ -660,10 +670,12 @@ SnapTranslator.dict.zh_CN = {
|
|||
'尖端',
|
||||
'middle':
|
||||
'中间',
|
||||
'paste %spr :
|
||||
"把自己黏贴到 %spr 上"
|
||||
'paste on %spr':
|
||||
'拼贴在 %spr 上',
|
||||
'pen vectors':
|
||||
'画笔矢量',
|
||||
|
||||
// control:
|
||||
// control:
|
||||
'when %greenflag clicked':
|
||||
'当 %greenflag 被点击',
|
||||
'when %keyHat key pressed':
|
||||
|
@ -683,7 +695,9 @@ SnapTranslator.dict.zh_CN = {
|
|||
'scrolled-up':
|
||||
'向上滚动滚轮',
|
||||
'scrolled-down':
|
||||
'向下滚动滚轮',
|
||||
'向下滚动滚轮',
|
||||
'stopped':
|
||||
'停止',
|
||||
'when %b':
|
||||
'当 %b',
|
||||
'when I receive %msgHat':
|
||||
|
@ -692,6 +706,8 @@ SnapTranslator.dict.zh_CN = {
|
|||
'广播 %msg',
|
||||
'broadcast %msg and wait':
|
||||
'广播 %msg 并等待',
|
||||
'send %msg to %spr':
|
||||
'发送消息 %msg 给 %spr',
|
||||
'Message name':
|
||||
'消息名称',
|
||||
'message':
|
||||
|
@ -792,6 +808,8 @@ SnapTranslator.dict.zh_CN = {
|
|||
'对象 %self',
|
||||
'distance to %dst':
|
||||
'到 %dst 的距离',
|
||||
'distance':
|
||||
'距离',
|
||||
'reset timer':
|
||||
'计时器归零',
|
||||
'timer':
|
||||
|
@ -838,6 +856,16 @@ SnapTranslator.dict.zh_CN = {
|
|||
'于',
|
||||
'microphone %audio':
|
||||
'麦克风 %audio',
|
||||
'Microphone resolution...':
|
||||
'麦克风分辨率...',
|
||||
'Microphone':
|
||||
'麦克风',
|
||||
'low':
|
||||
'低',
|
||||
'high':
|
||||
'高',
|
||||
'max':
|
||||
'最大',
|
||||
'note':
|
||||
'音符',
|
||||
'frequency':
|
||||
|
@ -865,7 +893,11 @@ SnapTranslator.dict.zh_CN = {
|
|||
'video capture':
|
||||
'视频捕捉',
|
||||
'mirror video':
|
||||
'视频镜像',
|
||||
'视频镜像',
|
||||
'frames':
|
||||
'帧',
|
||||
'log pen vectors':
|
||||
'记录画笔矢量',
|
||||
|
||||
// operators:
|
||||
'%n mod %n':
|
||||
|
@ -1315,6 +1347,8 @@ SnapTranslator.dict.zh_CN = {
|
|||
'浅色表格线',
|
||||
'check for higher contrast\ntable views':
|
||||
'深色表格线',
|
||||
'Visible stepping':
|
||||
'可视化单步运行',
|
||||
'Thread safe scripts':
|
||||
'线程安全的脚本',
|
||||
'uncheck to allow\nscript reentrance':
|
||||
|
|
46
snap.html
46
snap.html
|
@ -2,30 +2,30 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Snap! Build Your Own Blocks 6.0.0 - alpha -</title>
|
||||
<title>Snap! Build Your Own Blocks 6.0.0 - beta -</title>
|
||||
<link rel="shortcut icon" href="src/favicon.ico">
|
||||
<script type="text/javascript" src="src/morphic.js?version=2020-02-10"></script>
|
||||
<script type="text/javascript" src="src/symbols.js?version=2020-02-10"></script>
|
||||
<script type="text/javascript" src="src/widgets.js?version=2020-01-03"></script>
|
||||
<script type="text/javascript" src="src/blocks.js?version=2020-02-11"></script>
|
||||
<script type="text/javascript" src="src/threads.js?version=2019-12-19"></script>
|
||||
<script type="text/javascript" src="src/objects.js?version=2020-01-28"></script>
|
||||
<script type="text/javascript" src="src/gui.js?version=2020-01-28"></script>
|
||||
<script type="text/javascript" src="src/paint.js?version=2019-06-27"></script>
|
||||
<script type="text/javascript" src="src/lists.js?version=2019-12-08"></script>
|
||||
<script type="text/javascript" src="src/byob.js?version=2020-01-03"></script>
|
||||
<script type="text/javascript" src="src/tables.js?version=2020-01-03"></script>
|
||||
<script type="text/javascript" src="src/sketch.js?version=2019-10-09"></script>
|
||||
<script type="text/javascript" src="src/video.js?version=2019-06-27"></script>
|
||||
<script type="text/javascript" src="src/maps.js?version=2019-10-28"></script>
|
||||
<script type="text/javascript" src="src/xml.js?version=2019-06-27"></script>
|
||||
<script type="text/javascript" src="src/store.js?version=2019-12-09"></script>
|
||||
<script type="text/javascript" src="src/locale.js?version=2020-01-28"></script>
|
||||
<script type="text/javascript" src="src/cloud.js?version=2019-10-09"></script>
|
||||
<script type="text/javascript" src="src/api.js?version=2019-12-18"></script>
|
||||
<script type="text/javascript" src="src/sha512.js?version=2019-06-27"></script>
|
||||
<script type="text/javascript" src="src/FileSaver.min.js?version=2019-06-27"></script>
|
||||
<script type="text/javascript">
|
||||
<script src="src/morphic.js?version=2020-07-01"></script>
|
||||
<script src="src/symbols.js?version=2020-07-01"></script>
|
||||
<script src="src/widgets.js?version=2020-07-01"></script>
|
||||
<script src="src/blocks.js?version=2020-07-02"></script>
|
||||
<script src="src/threads.js?version=2020-07-02"></script>
|
||||
<script src="src/objects.js?version=2020-07-01"></script>
|
||||
<script src="src/gui.js?version=2020-07-01"></script>
|
||||
<script src="src/paint.js?version=2020-05-17"></script>
|
||||
<script src="src/lists.js?version=2020-07-01"></script>
|
||||
<script src="src/byob.js?version=2020-07-01"></script>
|
||||
<script src="src/tables.js?version=2020-05-18"></script>
|
||||
<script src="src/sketch.js?version=2020-04-15"></script>
|
||||
<script src="src/video.js?version=2019-06-27"></script>
|
||||
<script src="src/maps.js?version=2020-03-25"></script>
|
||||
<script src="src/xml.js?version=2020-04-27"></script>
|
||||
<script src="src/store.js?version=2020-05-18"></script>
|
||||
<script src="src/locale.js?version=2020-06-26"></script>
|
||||
<script src="src/cloud.js?version=2020-05-17"></script>
|
||||
<script src="src/api.js?version=2020-04-27"></script>
|
||||
<script src="src/sha512.js?version=2019-06-27"></script>
|
||||
<script src="src/FileSaver.min.js?version=2019-06-27"></script>
|
||||
<script>
|
||||
var world;
|
||||
window.onload = function () {
|
||||
world = new WorldMorph(document.getElementById('world'));
|
||||
|
|
312
src/blocks.js
312
src/blocks.js
|
@ -141,14 +141,14 @@ String, StringMorph, TextMorph, contains, degrees, detect, PianoMenuMorph,
|
|||
document, getDocumentPositionOf, isNaN, isString, newCanvas, nop, parseFloat,
|
||||
radians, useBlurredShadows, SpeechBubbleMorph, modules, StageMorph, Sound,
|
||||
fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph, Rectangle,
|
||||
DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph,
|
||||
DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, WHITE, BLACK,
|
||||
Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil,
|
||||
isSnapObject, PushButtonMorph, SpriteIconMorph, Process, AlignmentMorph,
|
||||
CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/
|
||||
|
||||
// Global stuff ////////////////////////////////////////////////////////
|
||||
|
||||
modules.blocks = '2020-May-12';
|
||||
modules.blocks = '2020-July-02';
|
||||
|
||||
var SyntaxElementMorph;
|
||||
var BlockMorph;
|
||||
|
@ -489,9 +489,6 @@ SyntaxElementMorph.prototype.revertToDefaultInput = function (arg, noValues) {
|
|||
}
|
||||
} else if (this instanceof MultiArgMorph) {
|
||||
deflt = this.labelPart(this.slotSpec);
|
||||
if (this.slotSpec === '%cs') {
|
||||
deflt.isStatic = true; // disable dropping reporters
|
||||
}
|
||||
} else if (this instanceof ReporterSlotMorph) {
|
||||
deflt = this.emptySlot();
|
||||
}
|
||||
|
@ -881,7 +878,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
break;
|
||||
case '%br':
|
||||
part = new Morph();
|
||||
part.setExtent(new Point(0, 0));
|
||||
part.setExtent(ZERO);
|
||||
part.isBlockLabelBreak = true;
|
||||
part.getSpec = () => '%br';
|
||||
break;
|
||||
|
@ -1329,6 +1326,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
'e^' : ['e^'],
|
||||
'10^' : ['10^'],
|
||||
'2^' : ['2^'],
|
||||
id: ['id']
|
||||
},
|
||||
true
|
||||
);
|
||||
|
@ -1569,10 +1567,10 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
case '%loopArrow':
|
||||
part = new SymbolMorph('loop');
|
||||
part.size = this.fontSize * 0.7;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%clr':
|
||||
|
@ -1627,40 +1625,40 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
case '%turtle':
|
||||
part = new SymbolMorph('turtle');
|
||||
part.size = this.fontSize * 1.2;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%turtleOutline':
|
||||
part = new SymbolMorph('turtleOutline');
|
||||
part.size = this.fontSize;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.isProtectedLabel = true; // doesn't participate in zebraing
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%clockwise':
|
||||
part = new SymbolMorph('turnRight');
|
||||
part.size = this.fontSize * 1.5;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.isProtectedLabel = false; // zebra colors
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%counterclockwise':
|
||||
part = new SymbolMorph('turnLeft');
|
||||
part.size = this.fontSize * 1.5;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.isProtectedLabel = false; // zebra colors
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%greenflag':
|
||||
|
@ -1670,7 +1668,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
part.isProtectedLabel = true; // doesn't participate in zebraing
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%stop':
|
||||
|
@ -1680,7 +1678,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
part.isProtectedLabel = true; // doesn't participate in zebraing
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%pause':
|
||||
|
@ -1690,26 +1688,26 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
part.isProtectedLabel = true; // doesn't participate in zebraing
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%blitz':
|
||||
part = new SymbolMorph('flash');
|
||||
part.size = this.fontSize;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.isProtectedLabel = false; // zebra colors
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
case '%list':
|
||||
part = new SymbolMorph('list');
|
||||
part.size = this.fontSize;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
break;
|
||||
|
||||
|
@ -1767,7 +1765,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
part.isProtectedLabel = tokens.length > 2; // zebra colors
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing;
|
||||
ZERO : this.embossing;
|
||||
part.fixLayout();
|
||||
} else {
|
||||
part = new StringMorph(
|
||||
|
@ -1778,9 +1776,9 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
|
|||
false, // italic
|
||||
false, // isNumeric
|
||||
MorphicPreferences.isFlat ?
|
||||
new Point() : this.embossing, // shadowOffset
|
||||
ZERO : this.embossing, // shadowOffset
|
||||
this.color.darker(this.labelContrast), // shadowColor
|
||||
new Color(255, 255, 255), // color
|
||||
WHITE, // color
|
||||
this.labelFontName // fontName
|
||||
);
|
||||
}
|
||||
|
@ -1806,7 +1804,6 @@ SyntaxElementMorph.prototype.fixLayout = function () {
|
|||
maxX = 0,
|
||||
blockWidth = this.minWidth,
|
||||
blockHeight,
|
||||
affected,
|
||||
l = [],
|
||||
lines = [],
|
||||
space = this.isPrototype ?
|
||||
|
@ -1815,8 +1812,7 @@ SyntaxElementMorph.prototype.fixLayout = function () {
|
|||
this.methodIconExtent().x + space : 0,
|
||||
bottomCorrection,
|
||||
hasLoopCSlot = false,
|
||||
hasLoopArrow = false,
|
||||
initialExtent = this.extent();
|
||||
hasLoopArrow = false;
|
||||
|
||||
if ((this instanceof MultiArgMorph) && (this.slotSpec !== '%cs')) {
|
||||
blockWidth += this.arrows().width();
|
||||
|
@ -2057,35 +2053,14 @@ SyntaxElementMorph.prototype.fixLayout = function () {
|
|||
}
|
||||
|
||||
// find out if one of my parents needs to be fixed
|
||||
if (this instanceof CommandBlockMorph) {
|
||||
if (this.height() !== initialExtent.y) {
|
||||
affected = this.parentThatIsA(CommandSlotMorph, ReporterSlotMorph);
|
||||
if (affected) {
|
||||
affected.fixLayout();
|
||||
}
|
||||
}
|
||||
if (this.width() !== initialExtent.x) {
|
||||
affected = this.parentThatIsA(
|
||||
ReporterBlockMorph,
|
||||
CommandSlotMorph,
|
||||
RingCommandSlotMorph,
|
||||
ReporterSlotMorph
|
||||
);
|
||||
if (affected) {
|
||||
affected.fixLayout();
|
||||
}
|
||||
}
|
||||
if (affected) {
|
||||
return;
|
||||
}
|
||||
} else if (this instanceof ReporterBlockMorph) {
|
||||
if (this.parent && this.parent.fixLayout) {
|
||||
this.parent.fixLayout();
|
||||
this.parent.changed();
|
||||
if (this instanceof BlockMorph && this.parent && this.parent.fixLayout) {
|
||||
this.parent.fixLayout();
|
||||
this.parent.changed();
|
||||
if (this.parent instanceof SyntaxElementMorph) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.fixHighlight();
|
||||
};
|
||||
|
||||
|
@ -2620,18 +2595,25 @@ BlockMorph.prototype.userMenu = function () {
|
|||
myself = this,
|
||||
shiftClicked = world.currentKey === 16,
|
||||
proc = this.activeProcess(),
|
||||
top = this.topBlock(),
|
||||
vNames = proc && proc.context && proc.context.outerContext ?
|
||||
proc.context.outerContext.variables.names() : [],
|
||||
alternatives,
|
||||
field,
|
||||
rcvr,
|
||||
top;
|
||||
rcvr;
|
||||
|
||||
function addOption(label, toggle, test, onHint, offHint) {
|
||||
var on = '\u2611 ',
|
||||
off = '\u2610 ';
|
||||
menu.addItem(
|
||||
(test ? on : off) + localize(label),
|
||||
[
|
||||
test ? new SymbolMorph(
|
||||
'checkedBox',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
) : new SymbolMorph(
|
||||
'rectangle',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
),
|
||||
localize(label)
|
||||
],
|
||||
toggle,
|
||||
test ? onHint : offHint
|
||||
);
|
||||
|
@ -2657,18 +2639,6 @@ BlockMorph.prototype.userMenu = function () {
|
|||
"help...",
|
||||
'showHelp'
|
||||
);
|
||||
if (shiftClicked) {
|
||||
top = this.topBlock();
|
||||
if (top instanceof ReporterBlockMorph) {
|
||||
menu.addItem(
|
||||
"script pic with result...",
|
||||
() => top.exportResultPic(),
|
||||
'open a new window\n' +
|
||||
'with a picture of both\nthis script and its result',
|
||||
new Color(100, 0, 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (this.isTemplate) {
|
||||
if (this.parent instanceof SyntaxElementMorph) { // in-line
|
||||
if (this.selector === 'reportGetVar') { // script var definition
|
||||
|
@ -2921,13 +2891,20 @@ BlockMorph.prototype.userMenu = function () {
|
|||
IDE_Morph
|
||||
);
|
||||
ide.saveCanvasAs(
|
||||
this.topBlock().scriptPic(),
|
||||
top.scriptPic(),
|
||||
(ide.projectName || localize('untitled')) + ' ' +
|
||||
localize('script pic')
|
||||
);
|
||||
},
|
||||
'open a new window\nwith a picture of this script'
|
||||
'save a picture\nof this script'
|
||||
);
|
||||
if (top instanceof ReporterBlockMorph) {
|
||||
menu.addItem(
|
||||
"result pic...",
|
||||
() => top.exportResultPic(),
|
||||
'save a picture of both\nthis script and its result'
|
||||
);
|
||||
}
|
||||
if (shiftClicked) {
|
||||
menu.addItem(
|
||||
'download script',
|
||||
|
@ -2971,7 +2948,6 @@ BlockMorph.prototype.userMenu = function () {
|
|||
if (this.parent.parentThatIsA(RingMorph)) {
|
||||
menu.addLine();
|
||||
menu.addItem("unringify", 'unringify');
|
||||
top = this.topBlock();
|
||||
if (this instanceof ReporterBlockMorph ||
|
||||
(!(top instanceof HatBlockMorph))) {
|
||||
menu.addItem("ringify", 'ringify');
|
||||
|
@ -2982,7 +2958,7 @@ BlockMorph.prototype.userMenu = function () {
|
|||
|| (this.parent instanceof CommandSlotMorph)
|
||||
|| (this instanceof HatBlockMorph)
|
||||
|| (this instanceof CommandBlockMorph
|
||||
&& (this.topBlock() instanceof HatBlockMorph))) {
|
||||
&& (top instanceof HatBlockMorph))) {
|
||||
return menu;
|
||||
}
|
||||
menu.addLine();
|
||||
|
@ -3175,6 +3151,10 @@ BlockMorph.prototype.unringify = function () {
|
|||
};
|
||||
|
||||
BlockMorph.prototype.relabel = function (alternativeSelectors) {
|
||||
// morph one block into another trying to keep the inputs in place
|
||||
// alternative Selector can either be a string representing
|
||||
// a block selector or a 2-item array containing a string and
|
||||
// an integer offset for restoring inputs
|
||||
var menu, oldInputs,
|
||||
target = this.selectForEdit(); // copy-on-edit
|
||||
if (target !== this) {
|
||||
|
@ -3182,14 +3162,22 @@ BlockMorph.prototype.relabel = function (alternativeSelectors) {
|
|||
}
|
||||
menu = new MenuMorph(this);
|
||||
oldInputs = this.inputs();
|
||||
alternativeSelectors.forEach(sel => {
|
||||
var block = SpriteMorph.prototype.blockForSelector(sel);
|
||||
block.restoreInputs(oldInputs);
|
||||
alternativeSelectors.forEach(alternative => {
|
||||
var block, selector, offset;
|
||||
if (alternative instanceof Array) {
|
||||
selector = alternative[0];
|
||||
offset = -alternative[1];
|
||||
} else {
|
||||
selector = alternative;
|
||||
offset = 0;
|
||||
}
|
||||
block = SpriteMorph.prototype.blockForSelector(selector, true);
|
||||
block.restoreInputs(oldInputs, offset);
|
||||
block.fixBlockColor(null, true);
|
||||
block.addShadow(new Point(3, 3));
|
||||
menu.addItem(
|
||||
block.fullImage(),
|
||||
() => this.setSelector(sel)
|
||||
() => this.setSelector(selector, -offset)
|
||||
);
|
||||
});
|
||||
menu.popup(this.world(), this.bottomLeft().subtract(new Point(
|
||||
|
@ -3198,17 +3186,38 @@ BlockMorph.prototype.relabel = function (alternativeSelectors) {
|
|||
)));
|
||||
};
|
||||
|
||||
BlockMorph.prototype.setSelector = function (aSelector) {
|
||||
|
||||
BlockMorph.prototype.setSelector = function (aSelector, inputOffset = 0) {
|
||||
// private - used only for relabel()
|
||||
// input offset is optional and can be used to shift the inputs
|
||||
// to be restored
|
||||
var oldInputs = this.inputs(),
|
||||
scripts = this.parentThatIsA(ScriptsMorph),
|
||||
surplus,
|
||||
info;
|
||||
info,
|
||||
slots,
|
||||
i;
|
||||
info = SpriteMorph.prototype.blocks[aSelector];
|
||||
this.setCategory(info.category);
|
||||
this.selector = aSelector;
|
||||
this.setSpec(localize(info.spec));
|
||||
surplus = this.restoreInputs(oldInputs);
|
||||
this.defaults = info.defaults || [];
|
||||
|
||||
// restore default values
|
||||
slots = this.inputs();
|
||||
if (slots[0] instanceof MultiArgMorph) {
|
||||
slots[0].setContents(this.defaults);
|
||||
slots[0].defaults = this.defaults;
|
||||
} else {
|
||||
for (i = 0; i < this.defaults.length; i += 1) {
|
||||
if (this.defaults[i] !== null && slots[i].setContents) {
|
||||
slots[i].setContents(this.defaults[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore previous inputs
|
||||
surplus = this.restoreInputs(oldInputs, -inputOffset);
|
||||
this.fixLabelColor();
|
||||
|
||||
// place surplus blocks on scipts
|
||||
|
@ -3220,19 +3229,37 @@ BlockMorph.prototype.setSelector = function (aSelector) {
|
|||
}
|
||||
};
|
||||
|
||||
BlockMorph.prototype.restoreInputs = function (oldInputs) {
|
||||
BlockMorph.prototype.restoreInputs = function (oldInputs, offset = 0) {
|
||||
// private - used only for relabel()
|
||||
// try to restore my previous inputs when my spec has been changed
|
||||
// return an Array of left-over blocks, if any
|
||||
var i = 0,
|
||||
old,
|
||||
nb,
|
||||
// optional offset parameter allows for shifting the range
|
||||
// of inputs to be restored
|
||||
var old, nb, i,
|
||||
leftOver = [];
|
||||
|
||||
this.inputs().forEach(inp => {
|
||||
// gather leading surplus blocks
|
||||
for (i = 0; i < offset; i += 1) {
|
||||
old = oldInputs[i];
|
||||
if (old instanceof ReporterBlockMorph) {
|
||||
this.replaceInput(inp, old.fullCopy());
|
||||
leftOver.push(old);
|
||||
} else if (old instanceof CommandSlotMorph) {
|
||||
nb = old.nestedBlock();
|
||||
if (nb) {
|
||||
leftOver.push(nb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore matching inputs in their original order
|
||||
this.inputs().forEach(inp => {
|
||||
old = oldInputs[offset];
|
||||
if (old instanceof ReporterBlockMorph) {
|
||||
if (inp instanceof TemplateSlotMorph || inp.isStatic) {
|
||||
leftOver.push(old);
|
||||
} else {
|
||||
this.replaceInput(inp, old.fullCopy());
|
||||
}
|
||||
} else if (old && inp instanceof InputSlotMorph) {
|
||||
// original - turns empty numberslots to 0:
|
||||
// inp.setContents(old.evaluate());
|
||||
|
@ -3249,12 +3276,12 @@ BlockMorph.prototype.restoreInputs = function (oldInputs) {
|
|||
inp.nestedBlock(nb.fullCopy());
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
offset += 1;
|
||||
});
|
||||
|
||||
// gather surplus blocks
|
||||
for (i; i < oldInputs.length; i += 1) {
|
||||
old = oldInputs[i];
|
||||
// gather trailing surplus blocks
|
||||
for (offset; offset < oldInputs.length; offset += 1) {
|
||||
old = oldInputs[offset];
|
||||
if (old instanceof ReporterBlockMorph) {
|
||||
leftOver.push(old);
|
||||
} else if (old instanceof CommandSlotMorph) {
|
||||
|
@ -4166,7 +4193,7 @@ BlockMorph.prototype.forceNormalColoring = function () {
|
|||
var clr = SpriteMorph.prototype.blockColor[this.category];
|
||||
this.setColor(clr);
|
||||
this.setLabelColor(
|
||||
new Color(255, 255, 255),
|
||||
WHITE,
|
||||
clr.darker(this.labelContrast),
|
||||
MorphicPreferences.isFlat ? ZERO : this.embossing
|
||||
);
|
||||
|
@ -4200,13 +4227,13 @@ BlockMorph.prototype.fixLabelColor = function () {
|
|||
var clr = SpriteMorph.prototype.blockColor[this.category];
|
||||
if (this.color.eq(clr)) {
|
||||
this.setLabelColor(
|
||||
new Color(255, 255, 255),
|
||||
WHITE,
|
||||
clr.darker(this.labelContrast),
|
||||
MorphicPreferences.isFlat ? null : this.embossing
|
||||
);
|
||||
} else {
|
||||
this.setLabelColor(
|
||||
new Color(0, 0, 0),
|
||||
BLACK,
|
||||
clr.lighter(this.zebraContrast)
|
||||
.lighter(this.labelContrast * 2),
|
||||
MorphicPreferences.isFlat ? null : this.embossing.neg()
|
||||
|
@ -6354,7 +6381,7 @@ ScriptsMorph.prototype.cleanUpSpacing = 15;
|
|||
ScriptsMorph.prototype.isPreferringEmptySlots = true;
|
||||
ScriptsMorph.prototype.enableKeyboard = true;
|
||||
ScriptsMorph.prototype.enableNestedAutoWrapping = true;
|
||||
ScriptsMorph.prototype.feedbackColor = new Color(255, 255, 255);
|
||||
ScriptsMorph.prototype.feedbackColor = WHITE;
|
||||
|
||||
// ScriptsMorph instance creation:
|
||||
|
||||
|
@ -6617,7 +6644,7 @@ ScriptsMorph.prototype.closestInput = function (reporter, hand) {
|
|||
!input.isLocked() &&
|
||||
input.bounds.intersects(fb) &&
|
||||
!contains(blackList, input) &&
|
||||
touchingVariadicArrowsIfAny(input)
|
||||
touchingVariadicArrowsIfAny(input, handPos)
|
||||
);
|
||||
if (target) {
|
||||
return target;
|
||||
|
@ -6632,7 +6659,8 @@ ScriptsMorph.prototype.closestInput = function (reporter, hand) {
|
|||
!input.isLocked() &&
|
||||
input.bounds.containsPoint(handPos) &&
|
||||
!(input.parent instanceof PrototypeHatBlockMorph) &&
|
||||
!contains(blackList, input)
|
||||
!contains(blackList, input) &&
|
||||
touchingVariadicArrowsIfAny(input, handPos)
|
||||
);
|
||||
if (target) {
|
||||
return target;
|
||||
|
@ -6644,7 +6672,8 @@ ScriptsMorph.prototype.closestInput = function (reporter, hand) {
|
|||
!input.isLocked() &&
|
||||
input.fullBounds().intersects(fb) &&
|
||||
!(input.parent instanceof PrototypeHatBlockMorph) &&
|
||||
!contains(blackList, input)
|
||||
!contains(blackList, input) &&
|
||||
touchingVariadicArrowsIfAny(input)
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -6699,10 +6728,17 @@ ScriptsMorph.prototype.userMenu = function () {
|
|||
stage = obj.parentThatIsA(StageMorph);
|
||||
|
||||
function addOption(label, toggle, test, onHint, offHint) {
|
||||
var on = '\u2611 ',
|
||||
off = '\u2610 ';
|
||||
menu.addItem(
|
||||
(test ? on : off) + localize(label),
|
||||
[
|
||||
test ? new SymbolMorph(
|
||||
'checkedBox',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
) : new SymbolMorph(
|
||||
'rectangle',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
),
|
||||
localize(label)
|
||||
],
|
||||
toggle,
|
||||
test ? onHint : offHint
|
||||
);
|
||||
|
@ -6768,7 +6804,7 @@ ScriptsMorph.prototype.userMenu = function () {
|
|||
menu.addItem(
|
||||
'scripts pic...',
|
||||
'exportScriptsPicture',
|
||||
'open a new window\nwith a picture of all scripts'
|
||||
'save a picture\nof all scripts'
|
||||
);
|
||||
if (ide) {
|
||||
menu.addLine();
|
||||
|
@ -8156,6 +8192,10 @@ CSlotMorph.prototype.fixHolesLayout = function () {
|
|||
];
|
||||
};
|
||||
|
||||
CSlotMorph.prototype.isLocked = function () {
|
||||
return this.isStatic || this.parent instanceof MultiArgMorph;
|
||||
};
|
||||
|
||||
// CSlotMorph drawing:
|
||||
|
||||
CSlotMorph.prototype.render = function (ctx) {
|
||||
|
@ -8537,7 +8577,7 @@ InputSlotMorph.prototype.init = function (
|
|||
this.constant = null;
|
||||
|
||||
InputSlotMorph.uber.init.call(this, null, true);
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.add(contents);
|
||||
this.add(arrow);
|
||||
contents.isEditable = true;
|
||||
|
@ -8680,6 +8720,25 @@ InputSlotMorph.prototype.menuFromDict = function (
|
|||
menu.addLine();
|
||||
menu.items.push(dial);
|
||||
menu.addLine();
|
||||
} else if (key.indexOf('§_') === 0) {
|
||||
// prefixing a key with '§_' only makes the menu item
|
||||
// appear when the user holds down the shift-key
|
||||
// use with care because mobile devices might only
|
||||
// have a "soft" keyboard that isn't always there
|
||||
if (this.world().currentKey === 16) { // shift
|
||||
menu.addItem(
|
||||
key.slice(2),
|
||||
choices[key],
|
||||
null, // hint
|
||||
null, // color
|
||||
null, // bold
|
||||
true, // italic
|
||||
null, // doubleClickAction
|
||||
null, // shortcut
|
||||
!(choices[key] instanceof Array) &&
|
||||
typeof choices[key] !== 'function' // verbatim?
|
||||
);
|
||||
}
|
||||
} else if (key === '__shout__go__') {
|
||||
// show the green flag symbol
|
||||
flag = new SymbolMorph('flag');
|
||||
|
@ -8735,7 +8794,7 @@ InputSlotMorph.prototype.messagesMenu = function () {
|
|||
allNames = allNames.concat(morph.allMessageNames());
|
||||
}
|
||||
});
|
||||
allNames.forEach(name =>
|
||||
allNames.sort().forEach(name =>
|
||||
dict[name] = name
|
||||
);
|
||||
if (this.world().currentKey === 16) { // shift
|
||||
|
@ -8768,7 +8827,7 @@ InputSlotMorph.prototype.messagesReceivedMenu = function () {
|
|||
allNames = allNames.concat(morph.allMessageNames());
|
||||
}
|
||||
});
|
||||
allNames.forEach(name => {
|
||||
allNames.sort().forEach(name => {
|
||||
if (name !== '__shout__go__') {
|
||||
dict[name] = name;
|
||||
}
|
||||
|
@ -9074,7 +9133,7 @@ InputSlotMorph.prototype.soundsMenu = function () {
|
|||
allNames = allNames.concat(sound.name)
|
||||
);
|
||||
if (allNames.length > 0) {
|
||||
allNames.forEach(name =>
|
||||
allNames.sort().forEach(name =>
|
||||
dict[name] = name
|
||||
);
|
||||
}
|
||||
|
@ -9176,9 +9235,9 @@ InputSlotMorph.prototype.setChoices = function (dict, readonly) {
|
|||
if (this.parent instanceof BlockMorph) {
|
||||
this.parent.fixLabelColor();
|
||||
if (!readonly) {
|
||||
cnts.shadowOffset = new Point();
|
||||
cnts.shadowOffset = ZERO;
|
||||
cnts.shadowColor = null;
|
||||
cnts.setColor(new Color(0, 0, 0));
|
||||
cnts.setColor(BLACK);
|
||||
}
|
||||
}
|
||||
this.fixLayout();
|
||||
|
@ -9196,10 +9255,10 @@ InputSlotMorph.prototype.fixLayout = function () {
|
|||
contents.isEditable = (!this.isReadOnly);
|
||||
if (this.isReadOnly) {
|
||||
contents.disableSelecting();
|
||||
contents.color = new Color(255, 255, 255);
|
||||
contents.color = WHITE;
|
||||
} else {
|
||||
contents.enableSelecting();
|
||||
contents.color = new Color(0, 0, 0);
|
||||
contents.color = BLACK;
|
||||
}
|
||||
|
||||
if (this.choices) {
|
||||
|
@ -10446,7 +10505,7 @@ ArrowMorph.prototype.init = function (direction, size, padding, color) {
|
|||
this.padding = padding || 0;
|
||||
|
||||
ArrowMorph.uber.init.call(this);
|
||||
this.color = color || new Color(0, 0, 0);
|
||||
this.color = color || BLACK;
|
||||
this.bounds.setWidth(this.size);
|
||||
this.bounds.setHeight(this.size);
|
||||
this.rerender();
|
||||
|
@ -10538,7 +10597,7 @@ TextSlotMorph.prototype.init = function (
|
|||
this.constant = null;
|
||||
|
||||
InputSlotMorph.uber.init.call(this, null, null, null, null, true); // sil.
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.add(contents);
|
||||
this.add(arrow);
|
||||
contents.isEditable = true;
|
||||
|
@ -11027,9 +11086,6 @@ MultiArgMorph.prototype.addInput = function (contents) {
|
|||
var i, name,
|
||||
newPart = this.labelPart(this.slotSpec),
|
||||
idx = this.children.length - 1;
|
||||
if (this.slotSpec === '%cs') {
|
||||
newPart.isStatic = true; // disable dropping reporters
|
||||
}
|
||||
if (contents) {
|
||||
newPart.setContents(contents);
|
||||
} else if (this.elementSpec === '%scriptVars' ||
|
||||
|
@ -11060,6 +11116,7 @@ MultiArgMorph.prototype.addInput = function (contents) {
|
|||
this.children.splice(idx, 0, newPart);
|
||||
newPart.fixLayout();
|
||||
this.fixLayout();
|
||||
return newPart;
|
||||
};
|
||||
|
||||
MultiArgMorph.prototype.removeInput = function () {
|
||||
|
@ -11318,7 +11375,7 @@ ArgLabelMorph.prototype.fixLayout = function () {
|
|||
|
||||
if (this.parent) {
|
||||
this.color = this.parent.color;
|
||||
shadowOffset = label.shadowOffset || new Point();
|
||||
shadowOffset = label.shadowOffset || ZERO;
|
||||
|
||||
// determine the shadow color for zebra coloring:
|
||||
if (shadowOffset.x < 0) {
|
||||
|
@ -12403,6 +12460,22 @@ CommentMorph.prototype.toggleExpand = function () {
|
|||
this.isCollapsed = !this.isCollapsed;
|
||||
this.fixLayout();
|
||||
this.align();
|
||||
if (!this.isCollapsed) {
|
||||
this.comeToFront();
|
||||
}
|
||||
};
|
||||
|
||||
CommentMorph.prototype.comeToFront = function () {
|
||||
if (this.parent) {
|
||||
this.parent.add(this);
|
||||
this.changed();
|
||||
}
|
||||
};
|
||||
|
||||
// CommentMorph events:
|
||||
|
||||
CommentMorph.prototype.mouseClickLeft = function () {
|
||||
this.comeToFront();
|
||||
};
|
||||
|
||||
// CommentMorph layout:
|
||||
|
@ -12411,6 +12484,7 @@ CommentMorph.prototype.layoutChanged = function () {
|
|||
// react to a change of the contents area
|
||||
this.fixLayout();
|
||||
this.align();
|
||||
this.comeToFront();
|
||||
};
|
||||
|
||||
CommentMorph.prototype.fixLayout = function () {
|
||||
|
@ -12506,7 +12580,7 @@ CommentMorph.prototype.userMenu = function () {
|
|||
localize('comment pic')
|
||||
);
|
||||
},
|
||||
'open a new window\nwith a picture of this comment'
|
||||
'save a picture\nof this comment'
|
||||
);
|
||||
return menu;
|
||||
};
|
||||
|
|
52
src/byob.js
52
src/byob.js
|
@ -96,7 +96,7 @@
|
|||
*/
|
||||
|
||||
/*global modules, CommandBlockMorph, SpriteMorph, TemplateSlotMorph, Map,
|
||||
StringMorph, Color, DialogBoxMorph, ScriptsMorph, ScrollFrameMorph,
|
||||
StringMorph, Color, DialogBoxMorph, ScriptsMorph, ScrollFrameMorph, WHITE,
|
||||
Point, HandleMorph, HatBlockMorph, BlockMorph, detect, List, Process,
|
||||
AlignmentMorph, ToggleMorph, InputFieldMorph, ReporterBlockMorph,
|
||||
StringMorph, nop, radians, BoxMorph, ArrowMorph, PushButtonMorph,
|
||||
|
@ -108,7 +108,7 @@ BooleanSlotMorph, XML_Serializer, SnapTranslator*/
|
|||
|
||||
// Global stuff ////////////////////////////////////////////////////////
|
||||
|
||||
modules.byob = '2020-May-07';
|
||||
modules.byob = '2020-July-01';
|
||||
|
||||
// Declarations
|
||||
|
||||
|
@ -344,7 +344,18 @@ CustomBlockDefinition.prototype.dropDownMenuOf = function (inputName) {
|
|||
|
||||
CustomBlockDefinition.prototype.parseChoices = function (string) {
|
||||
var dict = {},
|
||||
stack = [dict];
|
||||
stack = [dict],
|
||||
params, body;
|
||||
if (string.match(/^function\s*\(.*\)\s*{.*\n/)) {
|
||||
// It's a JS function definition.
|
||||
// Let's extract its params and body, and return a Function out of them.
|
||||
// if (!this.enableJS) {
|
||||
// throw new Error('JavaScript is not enabled');
|
||||
// }
|
||||
params = string.match(/^function\s*\((.*)\)/)[1].split(',');
|
||||
body = string.split('\n').slice(1,-1).join('\n');
|
||||
return Function.apply(null, params.concat([body]));
|
||||
}
|
||||
string.split('\n').forEach(line => {
|
||||
var pair = line.split('=');
|
||||
if (pair[0] === '}') {
|
||||
|
@ -947,7 +958,7 @@ CustomCommandBlockMorph.prototype.labelPart = function (spec) {
|
|||
} else {
|
||||
part = new BlockLabelFragmentMorph(spec);
|
||||
part.fontSize = this.fontSize;
|
||||
part.color = new Color(255, 255, 255);
|
||||
part.color = WHITE;
|
||||
part.isBold = true;
|
||||
part.shadowColor = this.color.darker(this.labelContrast);
|
||||
part.shadowOffset = this.embossing;
|
||||
|
@ -1711,7 +1722,7 @@ BlockDialogMorph.prototype.addCategoryButton = function (category) {
|
|||
button.labelShadowColor = colors[1];
|
||||
button.labelColor = IDE_Morph.prototype.buttonLabelColor;
|
||||
if (MorphicPreferences.isFlat) {
|
||||
button.labelPressColor = new Color(255, 255, 255);
|
||||
button.labelPressColor = WHITE;
|
||||
}
|
||||
button.contrast = this.buttonContrast;
|
||||
button.fixLayout();
|
||||
|
@ -2230,12 +2241,12 @@ BlockEditorMorph.prototype.refreshAllBlockInstances = function (oldSpec) {
|
|||
template = this.target.paletteBlockInstance(def);
|
||||
|
||||
if (this.definition.isGlobal) {
|
||||
this.target.allBlockInstances(this.definition).forEach(block =>
|
||||
block.refresh()
|
||||
this.target.allBlockInstances(this.definition).reverse().forEach(
|
||||
block => block.refresh()
|
||||
);
|
||||
} else {
|
||||
this.target.allDependentInvocationsOf(oldSpec).forEach(block =>
|
||||
block.refresh(def)
|
||||
this.target.allDependentInvocationsOf(oldSpec).reverse().forEach(
|
||||
block => block.refresh(def)
|
||||
);
|
||||
}
|
||||
if (template) {
|
||||
|
@ -2247,6 +2258,7 @@ BlockEditorMorph.prototype.updateDefinition = function () {
|
|||
var head, ide,
|
||||
oldSpec = this.definition.blockSpec(),
|
||||
pos = this.body.contents.position(),
|
||||
count = 0,
|
||||
element;
|
||||
|
||||
this.definition.receiver = this.target; // only for serialization
|
||||
|
@ -2286,6 +2298,13 @@ BlockEditorMorph.prototype.updateDefinition = function () {
|
|||
}
|
||||
|
||||
this.definition.body = this.context(head);
|
||||
|
||||
// make sure the spec is unique
|
||||
while (this.target.doubleDefinitionsFor(this.definition).length > 0) {
|
||||
count += 1;
|
||||
this.definition.spec = this.definition.spec + ' (' + count + ')';
|
||||
}
|
||||
|
||||
this.refreshAllBlockInstances(oldSpec);
|
||||
ide = this.target.parentThatIsA(IDE_Morph);
|
||||
ide.flushPaletteCache();
|
||||
|
@ -3116,6 +3135,13 @@ InputSlotDialogMorph.prototype.setType = function (fragmentType) {
|
|||
this.fragment.type = fragmentType || null;
|
||||
this.types.children.forEach(c => c.refresh());
|
||||
this.slots.children.forEach(c => c.refresh());
|
||||
if (isNil(fragmentType)) {
|
||||
this.isExpanded = false;
|
||||
this.types.children.forEach(c => c.refresh());
|
||||
this.changed();
|
||||
this.fixLayout();
|
||||
this.rerender();
|
||||
}
|
||||
this.edit();
|
||||
};
|
||||
|
||||
|
@ -3289,7 +3315,7 @@ InputSlotDialogMorph.prototype.createSlotTypeButtons = function () {
|
|||
// default values
|
||||
defLabel = new StringMorph(localize('Default Value:'));
|
||||
defLabel.fontSize = this.slots.radioButtonSingle.fontSize;
|
||||
defLabel.setColor(new Color(255, 255, 255));
|
||||
defLabel.setColor(WHITE);
|
||||
defLabel.refresh = () => {
|
||||
if (this.isExpanded && contains(
|
||||
[
|
||||
|
@ -3360,7 +3386,7 @@ InputSlotDialogMorph.prototype.createSlotTypeButtons = function () {
|
|||
new SymbolMorph(
|
||||
'loop',
|
||||
this.fontSize * 0.7,
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
),
|
||||
null // builder method that constructs the element morph
|
||||
);
|
||||
|
@ -3455,7 +3481,7 @@ InputSlotDialogMorph.prototype.addSlotTypeButton = function (
|
|||
button.outlineGradient = this.buttonOutlineGradient;
|
||||
button.fixLayout();
|
||||
button.label.isBold = false;
|
||||
button.label.setColor(new Color(255, 255, 255));
|
||||
button.label.setColor(WHITE);
|
||||
this.slots.add(button);
|
||||
return button;
|
||||
};
|
||||
|
@ -3481,7 +3507,7 @@ InputSlotDialogMorph.prototype.addSlotArityButton = function (
|
|||
|
||||
button.fixLayout();
|
||||
// button.label.isBold = false;
|
||||
button.label.setColor(new Color(255, 255, 255));
|
||||
button.label.setColor(WHITE);
|
||||
this.slots.add(button);
|
||||
return button;
|
||||
};
|
||||
|
|
10
src/cloud.js
10
src/cloud.js
|
@ -34,7 +34,7 @@
|
|||
/*global modules, hex_sha512*/
|
||||
|
||||
modules = modules || {};
|
||||
modules.cloud = '2019-October-09';
|
||||
modules.cloud = '2020-May-17';
|
||||
|
||||
// Global stuff
|
||||
|
||||
|
@ -461,6 +461,14 @@ Cloud.prototype.getPublishedProjectList = function (
|
|||
(username ? '/' + encodeURIComponent(username) : '') +
|
||||
'?ispublished=true';
|
||||
|
||||
if (!username) {
|
||||
// When requesting the global list of published projects, filter out
|
||||
// those with project names that are typical of online courses like
|
||||
// Teals or BJC. When requesting a user's published projects, show them
|
||||
// all.
|
||||
path += '&filtered=true';
|
||||
}
|
||||
|
||||
if (withThumbnail) {
|
||||
path += '&withthumbnail=true';
|
||||
}
|
||||
|
|
181
src/gui.js
181
src/gui.js
|
@ -71,14 +71,14 @@ InputSlotDialogMorph, ScriptsMorph, isNil, SymbolMorph, fontHeight, localize,
|
|||
BlockExportDialogMorph, BlockImportDialogMorph, SnapTranslator, List, ArgMorph,
|
||||
Uint8Array, HandleMorph, SVG_Costume, TableDialogMorph, CommentMorph, saveAs,
|
||||
CommandBlockMorph, BooleanSlotMorph, RingReporterSlotMorph, ScriptFocusMorph,
|
||||
BlockLabelPlaceHolderMorph, SpeechBubbleMorph, XML_Element, WatcherMorph,
|
||||
BlockLabelPlaceHolderMorph, SpeechBubbleMorph, XML_Element, WatcherMorph, WHITE,
|
||||
BlockRemovalDialogMorph,TableMorph, isSnapObject, isRetinaEnabled, SliderMorph,
|
||||
disableRetinaSupport, enableRetinaSupport, isRetinaSupported, MediaRecorder,
|
||||
Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note*/
|
||||
Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/
|
||||
|
||||
// Global stuff ////////////////////////////////////////////////////////
|
||||
|
||||
modules.gui = '2020-May-13';
|
||||
modules.gui = '2020-July-01';
|
||||
|
||||
// Declarations
|
||||
|
||||
|
@ -110,7 +110,7 @@ IDE_Morph.uber = Morph.prototype;
|
|||
|
||||
IDE_Morph.prototype.setDefaultDesign = function () {
|
||||
MorphicPreferences.isFlat = false;
|
||||
SpriteMorph.prototype.paletteColor = new Color(55, 55, 55);
|
||||
SpriteMorph.prototype.paletteColor = new Color(35, 35, 35);
|
||||
SpriteMorph.prototype.paletteTextColor = new Color(230, 230, 230);
|
||||
StageMorph.prototype.paletteTextColor
|
||||
= SpriteMorph.prototype.paletteTextColor;
|
||||
|
@ -119,22 +119,22 @@ IDE_Morph.prototype.setDefaultDesign = function () {
|
|||
= SpriteMorph.prototype.paletteColor.lighter(30);
|
||||
|
||||
IDE_Morph.prototype.buttonContrast = 30;
|
||||
IDE_Morph.prototype.backgroundColor = new Color(40, 40, 40);
|
||||
IDE_Morph.prototype.backgroundColor = new Color(10, 10, 10);
|
||||
IDE_Morph.prototype.frameColor = SpriteMorph.prototype.paletteColor;
|
||||
|
||||
IDE_Morph.prototype.groupColor
|
||||
= SpriteMorph.prototype.paletteColor.lighter(8);
|
||||
= SpriteMorph.prototype.paletteColor.lighter(5);
|
||||
IDE_Morph.prototype.sliderColor = SpriteMorph.prototype.sliderColor;
|
||||
IDE_Morph.prototype.buttonLabelColor = new Color(255, 255, 255);
|
||||
IDE_Morph.prototype.buttonLabelColor = WHITE;
|
||||
IDE_Morph.prototype.tabColors = [
|
||||
IDE_Morph.prototype.groupColor.darker(40),
|
||||
IDE_Morph.prototype.groupColor.darker(60),
|
||||
IDE_Morph.prototype.groupColor.darker(50),
|
||||
IDE_Morph.prototype.groupColor.darker(25),
|
||||
IDE_Morph.prototype.groupColor
|
||||
];
|
||||
IDE_Morph.prototype.rotationStyleColors = IDE_Morph.prototype.tabColors;
|
||||
IDE_Morph.prototype.appModeColor = new Color();
|
||||
IDE_Morph.prototype.appModeColor = BLACK;
|
||||
IDE_Morph.prototype.scriptsPaneTexture = this.scriptsTexture();
|
||||
IDE_Morph.prototype.padding = 5;
|
||||
IDE_Morph.prototype.padding = 1;
|
||||
|
||||
SpriteIconMorph.prototype.labelColor
|
||||
= IDE_Morph.prototype.buttonLabelColor;
|
||||
|
@ -146,12 +146,12 @@ IDE_Morph.prototype.setDefaultDesign = function () {
|
|||
= IDE_Morph.prototype.buttonLabelColor;
|
||||
|
||||
SyntaxElementMorph.prototype.contrast = 65;
|
||||
ScriptsMorph.prototype.feedbackColor = new Color(255, 255, 255);
|
||||
ScriptsMorph.prototype.feedbackColor = WHITE;
|
||||
};
|
||||
|
||||
IDE_Morph.prototype.setFlatDesign = function () {
|
||||
MorphicPreferences.isFlat = true;
|
||||
SpriteMorph.prototype.paletteColor = new Color(255, 255, 255);
|
||||
SpriteMorph.prototype.paletteColor = WHITE;
|
||||
SpriteMorph.prototype.paletteTextColor = new Color(70, 70, 70);
|
||||
StageMorph.prototype.paletteTextColor
|
||||
= SpriteMorph.prototype.paletteTextColor;
|
||||
|
@ -162,7 +162,7 @@ IDE_Morph.prototype.setFlatDesign = function () {
|
|||
IDE_Morph.prototype.backgroundColor = new Color(220, 220, 230);
|
||||
IDE_Morph.prototype.frameColor = new Color(240, 240, 245);
|
||||
|
||||
IDE_Morph.prototype.groupColor = new Color(255, 255, 255);
|
||||
IDE_Morph.prototype.groupColor = WHITE;
|
||||
IDE_Morph.prototype.sliderColor = SpriteMorph.prototype.sliderColor;
|
||||
IDE_Morph.prototype.buttonLabelColor = new Color(70, 70, 70);
|
||||
IDE_Morph.prototype.tabColors = [
|
||||
|
@ -195,7 +195,7 @@ IDE_Morph.prototype.scriptsTexture = function () {
|
|||
for (i = 0; i < 100; i += 4) {
|
||||
ctx.fillStyle = this.frameColor.toString();
|
||||
ctx.fillRect(i, 0, 1, 100);
|
||||
ctx.fillStyle = this.groupColor.lighter(6).toString();
|
||||
ctx.fillStyle = this.groupColor.lighter(4).toString();
|
||||
ctx.fillRect(i + 1, 0, 1, 100);
|
||||
ctx.fillRect(i + 3, 0, 1, 100);
|
||||
ctx.fillStyle = this.groupColor.toString();
|
||||
|
@ -676,7 +676,7 @@ IDE_Morph.prototype.createLogo = function () {
|
|||
myself.snapMenu();
|
||||
};
|
||||
|
||||
this.logo.color = new Color();
|
||||
this.logo.color = BLACK;
|
||||
this.logo.setExtent(new Point(200, 28)); // dimensions are fixed
|
||||
this.add(this.logo);
|
||||
};
|
||||
|
@ -742,7 +742,7 @@ IDE_Morph.prototype.createControlBar = function () {
|
|||
button.labelShadowOffset = new Point(-1, -1);
|
||||
button.labelShadowColor = colors[1];
|
||||
button.labelColor = MorphicPreferences.isFlat ?
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
: this.buttonLabelColor;
|
||||
button.contrast = this.buttonContrast;
|
||||
// button.hint = 'stage size\nsmall & normal';
|
||||
|
@ -1076,7 +1076,8 @@ IDE_Morph.prototype.createControlBar = function () {
|
|||
|
||||
this.controlBar.updateLabel = function () {
|
||||
var suffix = myself.world().isDevMode ?
|
||||
' - ' + localize('development mode') : '';
|
||||
' - ' + localize('development mode') : '',
|
||||
txt;
|
||||
|
||||
if (this.label) {
|
||||
this.label.destroy();
|
||||
|
@ -1084,8 +1085,7 @@ IDE_Morph.prototype.createControlBar = function () {
|
|||
if (myself.isAppMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.label = new StringMorph(
|
||||
txt = new StringMorph(
|
||||
(myself.projectName || localize('untitled')) + suffix,
|
||||
14,
|
||||
'sans-serif',
|
||||
|
@ -1095,11 +1095,22 @@ IDE_Morph.prototype.createControlBar = function () {
|
|||
MorphicPreferences.isFlat ? null : new Point(2, 1),
|
||||
myself.frameColor.darker(myself.buttonContrast)
|
||||
);
|
||||
this.label.color = myself.buttonLabelColor;
|
||||
this.label.fixLayout();
|
||||
this.add(this.label);
|
||||
txt.color = myself.buttonLabelColor;
|
||||
|
||||
this.label = new FrameMorph();
|
||||
this.label.acceptsDrops = false;
|
||||
this.label.alpha = 0;
|
||||
txt.setPosition(this.label.position());
|
||||
this.label.add(txt);
|
||||
this.label.setExtent(
|
||||
new Point(
|
||||
steppingButton.left() - settingsButton.right() - padding * 2,
|
||||
txt.height()
|
||||
)
|
||||
);
|
||||
this.label.setCenter(this.center());
|
||||
this.label.setLeft(this.settingsButton.right() + padding);
|
||||
this.add(this.label);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1146,7 +1157,7 @@ IDE_Morph.prototype.createCategories = function () {
|
|||
button.labelShadowColor = colors[1];
|
||||
button.labelColor = myself.buttonLabelColor;
|
||||
if (MorphicPreferences.isFlat) {
|
||||
button.labelPressColor = new Color(255, 255, 255);
|
||||
button.labelPressColor = WHITE;
|
||||
}
|
||||
button.fixLayout();
|
||||
button.refresh();
|
||||
|
@ -1313,7 +1324,11 @@ IDE_Morph.prototype.createSpriteBar = function () {
|
|||
tabColors = this.tabColors,
|
||||
tabBar = new AlignmentMorph('row', -tabCorner * 2),
|
||||
tab,
|
||||
symbols = ['\u2192', '\u21BB', '\u2194'],
|
||||
symbols = [
|
||||
new SymbolMorph('arrowRightThin', 10),
|
||||
new SymbolMorph('turnAround', 10),
|
||||
new SymbolMorph('arrowLeftRightThin', 10),
|
||||
],
|
||||
labels = ['don\'t rotate', 'can rotate', 'only face left/right'],
|
||||
myself = this;
|
||||
|
||||
|
@ -1359,7 +1374,7 @@ IDE_Morph.prototype.createSpriteBar = function () {
|
|||
button.fixLayout();
|
||||
button.refresh();
|
||||
rotationStyleButtons.push(button);
|
||||
button.setPosition(myself.spriteBar.position().add(2));
|
||||
button.setPosition(myself.spriteBar.position().add(new Point(2, 4)));
|
||||
button.setTop(button.top()
|
||||
+ ((rotationStyleButtons.length - 1) * (button.height() + 2))
|
||||
);
|
||||
|
@ -1429,8 +1444,8 @@ IDE_Morph.prototype.createSpriteBar = function () {
|
|||
padlock.pressColor = tabColors[1];
|
||||
|
||||
padlock.tick.shadowOffset = MorphicPreferences.isFlat ?
|
||||
new Point() : new Point(-1, -1);
|
||||
padlock.tick.shadowColor = new Color(); // black
|
||||
ZERO : new Point(-1, -1);
|
||||
padlock.tick.shadowColor = BLACK;
|
||||
padlock.tick.color = this.buttonLabelColor;
|
||||
padlock.tick.isBold = false;
|
||||
padlock.tick.fixLayout();
|
||||
|
@ -1872,7 +1887,7 @@ IDE_Morph.prototype.fixLayout = function (situation) {
|
|||
this.spriteBar.setTop(this.logo.bottom() + padding);
|
||||
this.spriteBar.setExtent(new Point(
|
||||
Math.max(0, this.stage.left() - padding - this.spriteBar.left()),
|
||||
this.categories.bottom() - this.spriteBar.top() - padding
|
||||
this.categories.bottom() - this.spriteBar.top() - padding - 6
|
||||
));
|
||||
this.spriteBar.fixLayout();
|
||||
|
||||
|
@ -1966,7 +1981,8 @@ IDE_Morph.prototype.render = function (ctx) {
|
|||
frame = this.stage.bounds.translateBy(
|
||||
this.position().neg()
|
||||
).expandBy(2);
|
||||
ctx.strokeStyle = this.backgroundColor.toString();
|
||||
ctx.strokeStyle = (MorphicPreferences.isFlat ? this.backgroundColor
|
||||
: this.groupColor).toString();
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(frame.origin.x, frame.origin.y);
|
||||
|
@ -2254,7 +2270,9 @@ IDE_Morph.prototype.toggleRetina = function () {
|
|||
enableRetinaSupport();
|
||||
}
|
||||
this.world().fillPage();
|
||||
IDE_Morph.prototype.scriptsPaneTexture = this.scriptsTexture();
|
||||
if (!MorphicPreferences.isFlat) {
|
||||
IDE_Morph.prototype.scriptsPaneTexture = this.scriptsTexture();
|
||||
}
|
||||
this.stage.clearPenTrails();
|
||||
this.refreshIDE();
|
||||
};
|
||||
|
@ -2993,14 +3011,23 @@ IDE_Morph.prototype.settingsMenu = function () {
|
|||
stage = this.stage,
|
||||
world = this.world(),
|
||||
pos = this.controlBar.settingsButton.bottomLeft(),
|
||||
shiftClicked = (world.currentKey === 16);
|
||||
shiftClicked = (world.currentKey === 16),
|
||||
on = new SymbolMorph(
|
||||
'checkedBox',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
),
|
||||
off = new SymbolMorph(
|
||||
'rectangle',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
);
|
||||
|
||||
function addPreference(label, toggle, test, onHint, offHint, hide) {
|
||||
var on = '\u2611 ',
|
||||
off = '\u2610 ';
|
||||
if (!hide || shiftClicked) {
|
||||
menu.addItem(
|
||||
(test ? on : off) + localize(label),
|
||||
[
|
||||
(test? on : off),
|
||||
localize(label)
|
||||
],
|
||||
toggle,
|
||||
test ? onHint : offHint,
|
||||
hide ? new Color(100, 0, 0) : null
|
||||
|
@ -3419,7 +3446,7 @@ IDE_Morph.prototype.settingsMenu = function () {
|
|||
Process.prototype.enableHyperOps,
|
||||
'uncheck to disable\nusing operators on lists and tables',
|
||||
'check to enable\nusing operators on lists and tables',
|
||||
true
|
||||
false
|
||||
);
|
||||
addPreference(
|
||||
'Persist linked sublist IDs',
|
||||
|
@ -3509,8 +3536,7 @@ IDE_Morph.prototype.projectMenu = function () {
|
|||
menu.addItem(
|
||||
'Export blocks...',
|
||||
() => this.exportGlobalBlocks(),
|
||||
'show global custom block definitions as XML' +
|
||||
'\nin a new browser window'
|
||||
'save global custom block\ndefinitions as XML'
|
||||
);
|
||||
menu.addItem(
|
||||
'Unused blocks...',
|
||||
|
@ -3523,14 +3549,14 @@ IDE_Morph.prototype.projectMenu = function () {
|
|||
menu.addItem(
|
||||
'Export summary...',
|
||||
() => this.exportProjectSummary(),
|
||||
'open a new browser browser window\n with a summary of this project'
|
||||
'save a summary\nof this project'
|
||||
);
|
||||
|
||||
if (shiftClicked) {
|
||||
menu.addItem(
|
||||
'Export summary with drop-shadows...',
|
||||
() => this.exportProjectSummary(true),
|
||||
'open a new browser browser window' +
|
||||
'download and save' +
|
||||
'\nwith a summary of this project' +
|
||||
'\nwith drop-shadows on all pictures.' +
|
||||
'\nnot supported by all browsers',
|
||||
|
@ -3855,7 +3881,7 @@ IDE_Morph.prototype.aboutSnap = function () {
|
|||
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
|
||||
world = this.world();
|
||||
|
||||
aboutTxt = 'Snap! 6.0.0 - alpha -\nBuild Your Own Blocks\n\n'
|
||||
aboutTxt = 'Snap! 6.0.0 - beta -\nBuild Your Own Blocks\n\n'
|
||||
+ 'Copyright \u24B8 2008-2020 Jens M\u00F6nig and '
|
||||
+ 'Brian Harvey\n'
|
||||
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
|
||||
|
@ -3941,7 +3967,7 @@ IDE_Morph.prototype.aboutSnap = function () {
|
|||
null,
|
||||
null,
|
||||
MorphicPreferences.isFlat ? null : new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
),
|
||||
scroller,
|
||||
maxHeight = world.height() - dlg.titleFontSize * 10;
|
||||
|
@ -5353,19 +5379,34 @@ IDE_Morph.prototype.microphoneMenu = function () {
|
|||
world = this.world(),
|
||||
pos = this.controlBar.settingsButton.bottomLeft(),
|
||||
resolutions = ['low', 'normal', 'high', 'max'],
|
||||
microphone = this.stage.microphone;
|
||||
microphone = this.stage.microphone,
|
||||
tick = new SymbolMorph(
|
||||
'tick',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
),
|
||||
on = new SymbolMorph(
|
||||
'checkedBox',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
),
|
||||
empty = tick.fullCopy();
|
||||
|
||||
empty.render = nop;
|
||||
if (microphone.isReady) {
|
||||
menu.addItem(
|
||||
'\u2611 ' + localize('Microphone'),
|
||||
[
|
||||
on,
|
||||
localize('Microphone')
|
||||
],
|
||||
() => microphone.stop()
|
||||
);
|
||||
menu.addLine();
|
||||
}
|
||||
resolutions.forEach((res, i) => {
|
||||
menu.addItem(
|
||||
(microphone.resolution === i + 1 ? '\u2713 ' : ' ') +
|
||||
localize(res),
|
||||
[
|
||||
microphone.resolution === i + 1 ? tick : empty,
|
||||
localize(res)
|
||||
],
|
||||
() => microphone.setResolution(i + 1)
|
||||
);
|
||||
});
|
||||
|
@ -5377,11 +5418,20 @@ IDE_Morph.prototype.microphoneMenu = function () {
|
|||
IDE_Morph.prototype.languageMenu = function () {
|
||||
var menu = new MenuMorph(this),
|
||||
world = this.world(),
|
||||
pos = this.controlBar.settingsButton.bottomLeft();
|
||||
pos = this.controlBar.settingsButton.bottomLeft(),
|
||||
tick = new SymbolMorph(
|
||||
'tick',
|
||||
MorphicPreferences.menuFontSize * 0.75
|
||||
),
|
||||
empty = tick.fullCopy();
|
||||
|
||||
empty.render = nop;
|
||||
SnapTranslator.languages().forEach(lang =>
|
||||
menu.addItem(
|
||||
(SnapTranslator.language === lang ? '\u2713 ' : ' ') +
|
||||
SnapTranslator.languageName(lang),
|
||||
[
|
||||
SnapTranslator.language === lang ? tick : empty,
|
||||
SnapTranslator.languageName(lang)
|
||||
],
|
||||
() => {
|
||||
this.loadNewProject = false;
|
||||
this.setLanguage(lang);
|
||||
|
@ -5447,7 +5497,8 @@ IDE_Morph.prototype.userSetBlocksScale = function () {
|
|||
blck,
|
||||
shield,
|
||||
sample,
|
||||
action;
|
||||
action,
|
||||
dlg;
|
||||
|
||||
scrpt = new CommandBlockMorph();
|
||||
scrpt.color = SpriteMorph.prototype.blockColor.motion;
|
||||
|
@ -5487,10 +5538,14 @@ IDE_Morph.prototype.userSetBlocksScale = function () {
|
|||
scrpt.fullChanged();
|
||||
};
|
||||
|
||||
new DialogBoxMorph(
|
||||
dlg = new DialogBoxMorph(
|
||||
null,
|
||||
num => this.setBlocksScale(Math.min(num, 12))
|
||||
).withKey('zoomBlocks').prompt(
|
||||
).withKey('zoomBlocks');
|
||||
if (MorphicPreferences.isTouchDevice) {
|
||||
dlg.isDraggable = false;
|
||||
}
|
||||
dlg.prompt(
|
||||
'Zoom blocks',
|
||||
SyntaxElementMorph.prototype.scale.toString(),
|
||||
this.world(),
|
||||
|
@ -6184,7 +6239,7 @@ IDE_Morph.prototype.warnAboutIE = function () {
|
|||
null,
|
||||
null,
|
||||
MorphicPreferences.isFlat ? null : new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
);
|
||||
|
||||
dlg.key = 'IE-Warning';
|
||||
|
@ -6285,7 +6340,7 @@ ProjectDialogMorph.prototype.buildContents = function () {
|
|||
null, // width
|
||||
null, // font name
|
||||
new Point(1, 1), // shadow offset
|
||||
new Color(255, 255, 255) // shadowColor
|
||||
WHITE // shadowColor
|
||||
);
|
||||
notification.refresh = nop;
|
||||
this.srcBar.add(notification);
|
||||
|
@ -6495,7 +6550,7 @@ ProjectDialogMorph.prototype.addSourceButton = function (
|
|||
null,
|
||||
null,
|
||||
new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
),
|
||||
lbl2 = new StringMorph(
|
||||
label,
|
||||
|
@ -6506,7 +6561,7 @@ ProjectDialogMorph.prototype.addSourceButton = function (
|
|||
null,
|
||||
new Point(-1, -1),
|
||||
this.titleBarColor.darker(50),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
),
|
||||
l1 = new Morph(),
|
||||
l2 = new Morph(),
|
||||
|
@ -6529,7 +6584,7 @@ ProjectDialogMorph.prototype.addSourceButton = function (
|
|||
lbl2.add(new SymbolMorph(
|
||||
symbol,
|
||||
24,
|
||||
new Color(255, 255, 255),
|
||||
WHITE,
|
||||
new Point(-1, -1),
|
||||
this.titleBarColor.darker(50)
|
||||
));
|
||||
|
@ -7877,7 +7932,7 @@ SpriteIconMorph.uber = ToggleButtonMorph.prototype;
|
|||
SpriteIconMorph.prototype.thumbSize = new Point(40, 40);
|
||||
SpriteIconMorph.prototype.labelShadowOffset = null;
|
||||
SpriteIconMorph.prototype.labelShadowColor = null;
|
||||
SpriteIconMorph.prototype.labelColor = new Color(255, 255, 255);
|
||||
SpriteIconMorph.prototype.labelColor = WHITE;
|
||||
SpriteIconMorph.prototype.fontSize = 9;
|
||||
|
||||
// SpriteIconMorph instance creation:
|
||||
|
@ -8099,7 +8154,7 @@ SpriteIconMorph.prototype.userMenu = function () {
|
|||
this.object.name
|
||||
);
|
||||
},
|
||||
'open a new window\nwith a picture of the stage'
|
||||
'save a picture\nof the stage'
|
||||
);
|
||||
if (this.object.trailsLog.length) {
|
||||
menu.addItem(
|
||||
|
@ -8321,7 +8376,7 @@ CostumeIconMorph.uber = ToggleButtonMorph.prototype;
|
|||
CostumeIconMorph.prototype.thumbSize = new Point(80, 60);
|
||||
CostumeIconMorph.prototype.labelShadowOffset = null;
|
||||
CostumeIconMorph.prototype.labelShadowColor = null;
|
||||
CostumeIconMorph.prototype.labelColor = new Color(255, 255, 255);
|
||||
CostumeIconMorph.prototype.labelColor = WHITE;
|
||||
CostumeIconMorph.prototype.fontSize = 9;
|
||||
|
||||
// CostumeIconMorph instance creation:
|
||||
|
@ -8570,7 +8625,7 @@ TurtleIconMorph.uber = ToggleButtonMorph.prototype;
|
|||
TurtleIconMorph.prototype.thumbSize = new Point(80, 60);
|
||||
TurtleIconMorph.prototype.labelShadowOffset = null;
|
||||
TurtleIconMorph.prototype.labelShadowColor = null;
|
||||
TurtleIconMorph.prototype.labelColor = new Color(255, 255, 255);
|
||||
TurtleIconMorph.prototype.labelColor = WHITE;
|
||||
TurtleIconMorph.prototype.fontSize = 9;
|
||||
|
||||
// TurtleIconMorph instance creation:
|
||||
|
@ -8991,7 +9046,7 @@ SoundIconMorph.uber = ToggleButtonMorph.prototype;
|
|||
SoundIconMorph.prototype.thumbSize = new Point(80, 60);
|
||||
SoundIconMorph.prototype.labelShadowOffset = null;
|
||||
SoundIconMorph.prototype.labelShadowColor = null;
|
||||
SoundIconMorph.prototype.labelColor = new Color(255, 255, 255);
|
||||
SoundIconMorph.prototype.labelColor = WHITE;
|
||||
SoundIconMorph.prototype.fontSize = 9;
|
||||
|
||||
// SoundIconMorph instance creation:
|
||||
|
@ -9948,7 +10003,7 @@ SoundRecorderDialogMorph.prototype.ok = function () {
|
|||
SoundRecorderDialogMorph.prototype.destroy = function () {
|
||||
this.stop();
|
||||
this.audioElement.remove();
|
||||
if (this.mediaRecorder) {
|
||||
if (this.mediaRecorder && this.mediaRecorder.stream) {
|
||||
this.mediaRecorder.stream.getTracks()[0].stop();
|
||||
}
|
||||
SoundRecorderDialogMorph.uber.destroy.call(this);
|
||||
|
|
78
src/lists.js
78
src/lists.js
|
@ -58,11 +58,12 @@
|
|||
|
||||
/*global modules, BoxMorph, HandleMorph, PushButtonMorph, SyntaxElementMorph,
|
||||
Color, Point, WatcherMorph, StringMorph, SpriteMorph, ScrollFrameMorph, isNil,
|
||||
CellMorph, ArrowMorph, MenuMorph, snapEquals, localize, isString,
|
||||
CellMorph, ArrowMorph, MenuMorph, snapEquals, localize, isString, IDE_Morph,
|
||||
MorphicPreferences, TableDialogMorph, SpriteBubbleMorph, SpeechBubbleMorph,
|
||||
TableFrameMorph, TableMorph, Variable, isSnapObject, Costume, contains*/
|
||||
TableFrameMorph, TableMorph, Variable, isSnapObject, Costume, contains, detect,
|
||||
ZERO, WHITE*/
|
||||
|
||||
modules.lists = '2020-May-07';
|
||||
modules.lists = '2020-July-01';
|
||||
|
||||
var List;
|
||||
var ListWatcherMorph;
|
||||
|
@ -105,6 +106,10 @@ var ListWatcherMorph;
|
|||
asText() - answer my elements (recursively) concatenated
|
||||
asCSV() - answer a csv-formatted String of myself
|
||||
asJSON() - answer a json-formatted String of myself
|
||||
|
||||
utility:
|
||||
---------
|
||||
map(callback) - answer an arrayed copy applying a JS func to all
|
||||
*/
|
||||
|
||||
// List instance creation:
|
||||
|
@ -204,6 +209,12 @@ List.prototype.clear = function () {
|
|||
this.changed();
|
||||
};
|
||||
|
||||
List.prototype.map = function(callback) {
|
||||
return new List(
|
||||
this.asArray().map(callback)
|
||||
);
|
||||
};
|
||||
|
||||
// List getters (all hybrid):
|
||||
|
||||
List.prototype.length = function () {
|
||||
|
@ -602,6 +613,44 @@ List.prototype.hasOnlyAtomicData = function () {
|
|||
});
|
||||
};
|
||||
|
||||
// List-to-block (experimental)
|
||||
|
||||
List.prototype.blockify = function (limit = 500, count = [0]) {
|
||||
var block = SpriteMorph.prototype.blockForSelector('reportNewList'),
|
||||
slots = block.inputs()[0],
|
||||
len = this.length(),
|
||||
bool,
|
||||
i, value;
|
||||
|
||||
block.isDraggable = true;
|
||||
slots.removeInput();
|
||||
|
||||
// fill the slots with the data
|
||||
for (i = 0; i < len && count[0] < limit; i += 1) {
|
||||
value = this.at(i + 1);
|
||||
if (value instanceof List) {
|
||||
slots.replaceInput(
|
||||
slots.addInput(),
|
||||
value.blockify(limit, count)
|
||||
);
|
||||
} else if (typeof value === 'boolean') {
|
||||
bool = SpriteMorph.prototype.blockForSelector('reportBoolean');
|
||||
bool.inputs()[0].setContents(value);
|
||||
bool.isDraggable = true;
|
||||
slots.replaceInput(
|
||||
slots.addInput(),
|
||||
bool
|
||||
);
|
||||
} else {
|
||||
slots.addInput(value);
|
||||
}
|
||||
count[0] += 1;
|
||||
}
|
||||
|
||||
slots.fixBlockColor(null, true);
|
||||
return block;
|
||||
};
|
||||
|
||||
// ListWatcherMorph ////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
|
@ -645,8 +694,8 @@ ListWatcherMorph.prototype.init = function (list, parentCell) {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
MorphicPreferences.isFlat ? new Point() : new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
MorphicPreferences.isFlat ? ZERO : new Point(1, 1),
|
||||
WHITE
|
||||
);
|
||||
this.label.mouseClickLeft = function () {myself.startIndexMenu(); };
|
||||
|
||||
|
@ -810,8 +859,8 @@ ListWatcherMorph.prototype.update = function (anyway) {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
MorphicPreferences.isFlat ? new Point() : new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
MorphicPreferences.isFlat ? ZERO : new Point(1, 1),
|
||||
WHITE
|
||||
);
|
||||
cell = new CellMorph(
|
||||
this.list.at(idx),
|
||||
|
@ -860,6 +909,7 @@ ListWatcherMorph.prototype.updateLength = function (notDone) {
|
|||
} else {
|
||||
this.label.color = new Color(0, 0, 0);
|
||||
}
|
||||
this.label.fixLayout();
|
||||
this.label.setCenter(this.center());
|
||||
this.label.setBottom(this.bottom() - 3);
|
||||
};
|
||||
|
@ -957,6 +1007,20 @@ ListWatcherMorph.prototype.userMenu = function () {
|
|||
}
|
||||
var menu = new MenuMorph(this);
|
||||
menu.addItem('table view...', 'showTableView');
|
||||
if (this.list.canBeJSON()) {
|
||||
menu.addItem(
|
||||
'blockify',
|
||||
() => {
|
||||
var world = this.world(),
|
||||
ide = detect(world.children, m => m instanceof IDE_Morph);
|
||||
this.list.blockify().pickUp(world);
|
||||
world.hand.grabOrigin = {
|
||||
origin: ide.palette,
|
||||
position: ide.palette.center()
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
menu.addLine();
|
||||
menu.addItem(
|
||||
'open in dialog...',
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
/*global modules, contains*/
|
||||
|
||||
modules.locale = '2020-April-27';
|
||||
modules.locale = '2020-June-26';
|
||||
|
||||
// Global stuff
|
||||
|
||||
|
@ -162,7 +162,7 @@ SnapTranslator.dict.de = {
|
|||
'translator_e-mail':
|
||||
'jens@moenig.org, jadga.huegle@sap.com',
|
||||
'last_changed':
|
||||
'2020-01-28'
|
||||
'2020-06-08'
|
||||
};
|
||||
|
||||
SnapTranslator.dict.it = {
|
||||
|
@ -426,7 +426,7 @@ SnapTranslator.dict.bn = {
|
|||
'translator_e-mail':
|
||||
'mokter@gmail.com',
|
||||
'last_changed':
|
||||
'2014-07-02'
|
||||
'2020-06-25'
|
||||
};
|
||||
|
||||
SnapTranslator.dict.kn = {
|
||||
|
|
426
src/morphic.js
426
src/morphic.js
|
@ -26,55 +26,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
*** UNDER CONSTRUCTIOM ***
|
||||
|
||||
Morphic changes to v1:
|
||||
|
||||
* noticesTransparentClick => !isFreeForm (reversed default)
|
||||
* drawOn() / fullDrawOn() takes context instead of Canvas as first arg
|
||||
* drawNew() is deprecated => render(), also takes context as arg
|
||||
* rerender() to earmark for rerendering
|
||||
* image has a getter method: getImage()
|
||||
* image has been renamed to cachedImage
|
||||
* isCachingImage flag (default: false)
|
||||
* shouldRerender flag (default: false)
|
||||
* fixLayout() determines extent and arranges submorphs, if any, gets called
|
||||
from setExtent()
|
||||
* fixHolesLayout
|
||||
|
||||
"silent" - functions are no longer needed:
|
||||
|
||||
* silentSetExtent => bounds.setExtent()
|
||||
* silentMoveBy
|
||||
* silentSetPosition
|
||||
* silentSetWidth => bounds.setWidth()
|
||||
* silentSetHeight = bounds.setHeight()
|
||||
|
||||
likewise "silent" parameters are no longer needed and supported
|
||||
|
||||
* cachedFullImage
|
||||
* cachedFullBounds
|
||||
|
||||
are deprecated
|
||||
|
||||
"trackChanges" and other damage-list housekeeping tweaks are no longer
|
||||
needed and no longer supported, except for the Pen constructor's isWarped
|
||||
property and its methods, such as startWarp and endWarp.
|
||||
|
||||
Pen >> wantsRedraw is no longer needed and deprecated
|
||||
|
||||
holes:
|
||||
Morphs have a list of rectangles representing "untouchable" areas
|
||||
|
||||
* virtualKeyboard property and Morphic preference has been deprecated
|
||||
* fullImageClassic() => is always just fullImage()
|
||||
* keyboardReceiver => keyboardFocus
|
||||
|
||||
* keyboard navigation can be activated for any visible menu by pressing an arbitrary key
|
||||
|
||||
* new "noDropShadow" property for Morphs that already have built-in shadows (Menus, SpeechBubbles)
|
||||
* new "fullShadowSource" flag for Morphs, default is true, turn off (false) to only use the simple image instead of fullImage()
|
||||
|
||||
documentation contents
|
||||
----------------------
|
||||
I. inheritance hierarchy
|
||||
|
@ -100,6 +51,13 @@
|
|||
(h) text editing events
|
||||
(4) stepping
|
||||
(5) creating new kinds of morphs
|
||||
(a) drawing the shape
|
||||
(b) determining extent and arranging submorphs
|
||||
(c) pixel-perfect pointing events
|
||||
(d) caching the shape
|
||||
(e) holes
|
||||
(f) updating
|
||||
(g) duplicating
|
||||
(6) development and user modes
|
||||
(7) turtle graphics
|
||||
(8) supporting high-resolution "retina" screens
|
||||
|
@ -200,8 +158,7 @@
|
|||
III. yet to implement
|
||||
---------------------
|
||||
- keyboard support for scroll frames and lists
|
||||
- full keyboard support for menus (partial support exists)
|
||||
- virtual keyboard support for Android and IE
|
||||
- virtual keyboard support for Android
|
||||
|
||||
|
||||
IV. open issues
|
||||
|
@ -239,7 +196,7 @@
|
|||
* 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)
|
||||
redrawn at 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
|
||||
|
@ -248,7 +205,7 @@
|
|||
events)
|
||||
* a single text entry focus per World
|
||||
|
||||
In its current state morphic.js doesn't support Transforms (you
|
||||
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
|
||||
|
@ -280,11 +237,11 @@
|
|||
|
||||
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.
|
||||
and may also have a keyboard focus 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
|
||||
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.
|
||||
|
||||
|
@ -315,7 +272,6 @@
|
|||
|
||||
window.onload = function () {
|
||||
world = new WorldMorph(document.getElementById('world'));
|
||||
// +++ world.worldCanvas.focus();
|
||||
world.isDevMode = true;
|
||||
loop();
|
||||
};
|
||||
|
@ -328,7 +284,7 @@
|
|||
</head>
|
||||
<body style="margin: 0;">
|
||||
<canvas id="world" tabindex="1" width="800" height="600"
|
||||
style="position: absolute;" />
|
||||
style="position: absolute;"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -358,7 +314,7 @@
|
|||
<title>Morphic!</title>
|
||||
<script type="text/javascript" src="morphic.js"></script>
|
||||
<script type="text/javascript">
|
||||
var world1, world2;
|
||||
var world1, world2;
|
||||
|
||||
window.onload = function () {
|
||||
disableRetinaSupport();
|
||||
|
@ -370,7 +326,7 @@
|
|||
};
|
||||
|
||||
function loop() {
|
||||
requestAnimationFrame(loop);
|
||||
requestAnimationFrame(loop);
|
||||
world1.doOneCycle();
|
||||
world2.doOneCycle();
|
||||
}
|
||||
|
@ -378,9 +334,9 @@
|
|||
</head>
|
||||
<body>
|
||||
<p>first world:</p>
|
||||
<canvas id="world1" tabindex="1" width="600" height="400" />
|
||||
<canvas id="world1" tabindex="1" width="600" height="400"></canvas>
|
||||
<p>second world:</p>
|
||||
<canvas id="world2" tabindex="2" width="400" height="600" />
|
||||
<canvas id="world2" tabindex="2" width="400" height="600"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -411,7 +367,6 @@
|
|||
|
||||
worldCanvas = document.getElementById('world');
|
||||
world = new WorldMorph(worldCanvas);
|
||||
// +++ world.worldCanvas.focus();
|
||||
world.isDevMode = false;
|
||||
world.setColor(new Color());
|
||||
|
||||
|
@ -444,7 +399,7 @@
|
|||
</head>
|
||||
<body bgcolor='black' style="margin: 0;">
|
||||
<canvas id="world" width="800" height="600"
|
||||
style="position: absolute;" />
|
||||
style="position: absolute;"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -499,7 +454,7 @@
|
|||
events.
|
||||
|
||||
These system events are dispatched within the morphic World by the
|
||||
World's Hand and its keyboardReceiver (usually the active text
|
||||
World's Hand and its keyboardFocus (usually the active text
|
||||
cursor).
|
||||
|
||||
|
||||
|
@ -678,12 +633,12 @@
|
|||
droppedImage(aCanvas, name)
|
||||
droppedSVG(anImage, name)
|
||||
|
||||
events to interested Morphs at the mouse pointer. If you want you Morph
|
||||
events to interested Morphs at the mouse pointer. If you want your 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
|
||||
Morph's cachedImage 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
|
||||
|
@ -715,16 +670,16 @@
|
|||
(e) keyboard events
|
||||
-------------------
|
||||
The World dispatches the following key events to its active
|
||||
keyboardReceiver:
|
||||
keyboard focus:
|
||||
|
||||
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 morphs which acts as keyboard focus are
|
||||
CursorMorph - the basic text editing widget - and MenuMorph elements.
|
||||
If you wish to add keyboard support to your morph you need to add event
|
||||
handling methods for
|
||||
|
||||
processKeyPress(event)
|
||||
processKeyDown(event)
|
||||
|
@ -732,7 +687,7 @@
|
|||
|
||||
and activate them by assigning your morph to the World's
|
||||
|
||||
keyboardReceiver
|
||||
keyboardFocus
|
||||
|
||||
property.
|
||||
|
||||
|
@ -851,7 +806,7 @@
|
|||
|
||||
reactToSliderEdit(StringOrTextMorph)
|
||||
|
||||
events is dispatched, allowing for "Bret-Victor" style "live coding"
|
||||
events is dispatched, allowing for "Bret-Victor" style "scrubbing"
|
||||
applications.
|
||||
|
||||
In addition to user-initiated events text elements also emit
|
||||
|
@ -860,8 +815,8 @@
|
|||
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 only from instances of TextMorph
|
||||
fixLayout() - sent from instances of all Morphs, including StringMorphs
|
||||
|
||||
they are different so that Morphs which contain both multi-line and
|
||||
single-line text elements can hold them apart.
|
||||
|
@ -900,25 +855,156 @@
|
|||
--------------------------------
|
||||
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
|
||||
musical notes.
|
||||
|
||||
When you create your own morphs, you'll want to think about how to
|
||||
graphically render it, how to determine its size and whether it needs
|
||||
to arrange any other parts ("submorphs). There are also ways to specify
|
||||
its collision detection behavior and define "untouchable" regions
|
||||
("holes").
|
||||
|
||||
|
||||
(a) drawing the shape
|
||||
---------------------
|
||||
For this you have to override the default
|
||||
|
||||
render(ctx)
|
||||
|
||||
method.
|
||||
|
||||
This method draws the morph's shape with a 2d graphics context.
|
||||
This method draws the morph's shape using a given 2d graphics context.
|
||||
Note that any coordinates used in the render() method must be relative
|
||||
to the morph's own position, i.e. you don't need to worry about
|
||||
translating the shape yourself.
|
||||
|
||||
explain
|
||||
|
||||
* isCachingImage
|
||||
* isFreeForm = bool
|
||||
|
||||
Use the following template for a start:
|
||||
You can use the following template for a start:
|
||||
|
||||
MyMorph.prototype.render = function(ctx) {
|
||||
// use ctx to paint stuff here
|
||||
ctx.fillStyle = this.color.toString();
|
||||
ctx.fillRect(0, 0, this.width(), this.height());
|
||||
};
|
||||
|
||||
it renders the morph as a solid rectangle completely filling its
|
||||
area with its current color.
|
||||
|
||||
Notice how the coordinates for the fillRect() call are relative
|
||||
to the morph's own position: The rendered rectangle's origin is always
|
||||
located at (0, 0) regardless of the morph's actual position in the World.
|
||||
|
||||
|
||||
(b) determining extent and arranging submorphs
|
||||
----------------------------------------------
|
||||
If your new morph also needs to determine its extent and, e.g. to
|
||||
encompass one or several other morphs, or arrange the layout of its
|
||||
submorphs, make sure to also override the default
|
||||
|
||||
fixLayout()
|
||||
|
||||
method.
|
||||
|
||||
NOTE: If you need to set the morph's extent inside, in order to avoid
|
||||
infinite recursion instead of calling morph.setExtent() - which will
|
||||
in turn call morph.fixLayout() again - directly modify the morph's
|
||||
|
||||
bounds
|
||||
|
||||
property. Bounds is a rectable on which you can also use the same
|
||||
size-setters, e.g. by calling:
|
||||
|
||||
this.bounds.setExtent()
|
||||
|
||||
|
||||
(c) pixel-perfect pointing events
|
||||
---------------------------------
|
||||
In case your new morph needs to support pixel-perfect collision detection
|
||||
with other morphs or pointing devices such as the mouse or a stylus you
|
||||
can set the inherited attribute
|
||||
|
||||
isFreeForm = bool
|
||||
|
||||
to "true" (default is "false"). This makes sense the more your morph's
|
||||
visual shape diverges from a rectangle. For example, if you create a
|
||||
circular filled morph the default setting will register mouse-events
|
||||
anywhere within its bounding box, e.g. also in the transparent parts
|
||||
between the bounding box's corners outside of the circle's bounds.
|
||||
Instead you can specify your irregulary shaped morph to only register
|
||||
pointing events (mouse and touch) on solid, non-transparent parts.
|
||||
|
||||
Notice, however, that such pixel-perfect collision detection might
|
||||
strain processing resources, especially if applied liberally.
|
||||
|
||||
In order to mitigate unfavorable processor loads for pixel-perfect
|
||||
collision deteciton of irregularly shaped morphs there are two strategies
|
||||
to consider: Caching the shape and specifying "untouchable" regions.
|
||||
|
||||
|
||||
(d) caching the shape
|
||||
---------------------
|
||||
In case of pixel-perfect free-form collision detection it makes sense to
|
||||
cache your morph's current shape, so it doesn't have to be re-drawn onto a
|
||||
new Canvas element every time the mouse moves over its bounding box.
|
||||
For this you can set then inherited
|
||||
|
||||
isCachingImage = bool
|
||||
|
||||
attribute to "true" instead of the default "false" value. This will
|
||||
significantly speed up collision detection and smoothen animations that
|
||||
continuously perform collision detection. However, it will also consume
|
||||
more memory. Therefore it's best to use this setting with caution.
|
||||
|
||||
Snap! caches the shapes of sprites but not those of blocks. Instead it
|
||||
manages the insides of C- and E-shaped blocks through the morphic "holes"
|
||||
mechanism.
|
||||
|
||||
|
||||
(e) holes
|
||||
---------
|
||||
An alternative albeit not as precise and general way for handling
|
||||
irregularly shaped morphs with "untouchable" regions is to specify a set
|
||||
of rectangular areas in which pointing events (mouse or touch) are not
|
||||
registered.
|
||||
|
||||
By default the inherited
|
||||
|
||||
holes = []
|
||||
|
||||
property is an empty array. You can add one or more morphic Rectangle
|
||||
objects to this list, representing regions, in which occurring events will
|
||||
instead be passed on to the morph underneath.
|
||||
|
||||
Note that, same with the render() method, the coordinates of these
|
||||
rectangular holes must be specified relative to your morph's position.
|
||||
|
||||
If you specify holes you might find the need to adjust their layout
|
||||
depending on the layout of your morph. To accomplish this you can override
|
||||
the inherited
|
||||
|
||||
fixHolesLayout()
|
||||
|
||||
method.
|
||||
|
||||
|
||||
(f) updating
|
||||
------------
|
||||
One way for morphs to become alive is form them to literally "morph" their
|
||||
shape depending on whicher contest you wish them to react to. For example,
|
||||
you might want the user to interactively draw a shape using their fingers
|
||||
on a touch screen device, or you want the user to be able to "pinch" or
|
||||
otherwise distort a shape interactively. In all of these situations you'll
|
||||
want your morph to frequently rerender its shape.
|
||||
|
||||
You can accomplish this, by calling
|
||||
|
||||
rerender()
|
||||
|
||||
after every change to your morph's appearance that requires rerendering.
|
||||
|
||||
Such changes are usually only happening when the morph's dimensions or
|
||||
other visual properties - such as its color - changes.
|
||||
|
||||
|
||||
(g) duplicating
|
||||
---------------
|
||||
If your new morph stores or references to other morphs outside of
|
||||
the submorph tree in other properties, be sure to also override the
|
||||
default
|
||||
|
@ -996,18 +1082,18 @@
|
|||
the following properties of PenMorph are relevant for turtle
|
||||
graphics:
|
||||
|
||||
color - a Color
|
||||
color - a Color
|
||||
size - line width of pen trails
|
||||
heading - degrees
|
||||
isDown - drawing state
|
||||
heading - degrees
|
||||
isDown - drawing state
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -1182,12 +1268,17 @@
|
|||
|
||||
/*global window, HTMLCanvasElement, FileReader, Audio, FileList, Map*/
|
||||
|
||||
var morphicVersion = '2020-May-05';
|
||||
var morphicVersion = '2020-July-01';
|
||||
var modules = {}; // keep track of additional loaded modules
|
||||
var useBlurredShadows = true;
|
||||
|
||||
const ZERO = new Point();
|
||||
const BLACK = new Color();
|
||||
const WHITE = new Color(255, 255, 255);
|
||||
|
||||
Object.freeze(ZERO);
|
||||
Object.freeze(BLACK);
|
||||
Object.freeze(WHITE);
|
||||
|
||||
var standardSettings = {
|
||||
minimumFontHeight: getMinimumFontHeight(), // browser settings
|
||||
|
@ -1221,8 +1312,8 @@ var touchScreenSettings = {
|
|||
handleSize: 26,
|
||||
scrollBarSize: 24,
|
||||
mouseScrollAmount: 40,
|
||||
useSliderForInput: true,
|
||||
isTouchDevice: false,
|
||||
useSliderForInput: false,
|
||||
isTouchDevice: true,
|
||||
rasterizeSVGs: false,
|
||||
isFlat: false,
|
||||
grabThreshold: 5,
|
||||
|
@ -1348,7 +1439,7 @@ function newCanvas(extentPoint, nonRetina, recycleMe) {
|
|||
nonRetina = nonRetina || false;
|
||||
ext = (extentPoint ||
|
||||
(recycleMe ? new Point(recycleMe.width, recycleMe.height)
|
||||
: new Point(0, 0))).floor();
|
||||
: new Point(0, 0))).ceil();
|
||||
if (recycleMe &&
|
||||
!recycleMe.dataset.morphicShare &&
|
||||
(recycleMe.isRetinaEnabled || false) !== nonRetina &&
|
||||
|
@ -1412,24 +1503,11 @@ function getMinimumFontHeight() {
|
|||
}
|
||||
|
||||
function getDocumentPositionOf(aDOMelement) {
|
||||
// answer the absolute coordinates of a DOM element in the document
|
||||
var pos, offsetParent;
|
||||
if (aDOMelement === null) {
|
||||
return {x: 0, y: 0};
|
||||
}
|
||||
pos = {x: aDOMelement.offsetLeft, y: aDOMelement.offsetTop};
|
||||
offsetParent = aDOMelement.offsetParent;
|
||||
while (offsetParent !== null) {
|
||||
pos.x += offsetParent.offsetLeft;
|
||||
pos.y += offsetParent.offsetTop;
|
||||
if (offsetParent !== document.body &&
|
||||
offsetParent !== document.documentElement) {
|
||||
pos.x -= offsetParent.scrollLeft;
|
||||
pos.y -= offsetParent.scrollTop;
|
||||
}
|
||||
offsetParent = offsetParent.offsetParent;
|
||||
}
|
||||
return pos;
|
||||
// answer the relative coordinates of a DOM element in the viewport
|
||||
var rect = aDOMelement.getBoundingClientRect(),
|
||||
scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
|
||||
scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||
return {x: rect.left + scrollLeft, y:rect.top + scrollTop};
|
||||
}
|
||||
|
||||
function copy(target) {
|
||||
|
@ -2198,7 +2276,7 @@ Color.prototype.lighter = function (percent) {
|
|||
if (percent) {
|
||||
fract = (100 - percent) / 100;
|
||||
}
|
||||
return this.mixed(fract, new Color(255, 255, 255));
|
||||
return this.mixed(fract, WHITE);
|
||||
};
|
||||
|
||||
Color.prototype.dansDarker = function () {
|
||||
|
@ -2460,7 +2538,7 @@ Point.prototype.translateBy = function (deltaPoint) {
|
|||
};
|
||||
|
||||
Point.prototype.rotateBy = function (angle, centerPoint) {
|
||||
var center = centerPoint || new Point(0, 0),
|
||||
var center = centerPoint || ZERO,
|
||||
p = this.subtract(center),
|
||||
r = p.r(),
|
||||
theta = angle - p.theta();
|
||||
|
@ -2724,9 +2802,8 @@ Rectangle.prototype.round = function () {
|
|||
|
||||
Rectangle.prototype.spread = function () {
|
||||
// round me by applying floor() to my origin and ceil() to my corner
|
||||
// and expand by 1,
|
||||
// avoids artefacts on retina displays
|
||||
return this.origin.floor().corner(this.corner.ceil()).expandBy(1);
|
||||
return this.origin.floor().corner(this.corner.ceil());
|
||||
};
|
||||
|
||||
Rectangle.prototype.amountToTranslateWithin = function (aRect) {
|
||||
|
@ -3395,12 +3472,16 @@ Morph.prototype.fixLayout = function () {
|
|||
// implemented by my heirs
|
||||
// determine my extent and arrange my submorphs, if any
|
||||
// default is to do nothing
|
||||
// NOTE: If you need to set the extent, in order to avoid
|
||||
// infinite recursion instead of calling setExtent() (which will
|
||||
// in turn call fixLayout() again) directly modify the bounds
|
||||
// property, e.g. like this: this.bounds.setExtent()
|
||||
return;
|
||||
};
|
||||
|
||||
Morph.prototype.fixHolesLayout = function () {
|
||||
// implemented by my heirs
|
||||
// determine my extent and arrange my submorphs, if any
|
||||
// arrange my untouchable areas, if any
|
||||
// default is to do nothing
|
||||
return;
|
||||
};
|
||||
|
@ -3769,7 +3850,7 @@ Morph.prototype.overlappedMorphs = function () {
|
|||
Morph.prototype.getPixelColor = function (aPoint) {
|
||||
var point, context, data;
|
||||
point = aPoint.subtract(this.bounds.origin);
|
||||
context = this.cachedImage.getContext('2d');
|
||||
context = this.getImage().getContext('2d');
|
||||
data = context.getImageData(point.x, point.y, 1, 1);
|
||||
return new Color(
|
||||
data.data[0],
|
||||
|
@ -4609,7 +4690,7 @@ HandleMorph.prototype.init = function (
|
|||
this.type = type || 'resize'; // also: 'move', 'moveCenter', 'movePivot'
|
||||
this.isHighlighted = false;
|
||||
HandleMorph.uber.init.call(this);
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.isDraggable = false;
|
||||
if (this.type === 'movePivot') {
|
||||
size *= 2;
|
||||
|
@ -4650,7 +4731,7 @@ HandleMorph.prototype.render = function (ctx) {
|
|||
this.renderHandleOn(
|
||||
ctx,
|
||||
new Color(100, 100, 255),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
);
|
||||
} else {
|
||||
this.renderHandleOn(
|
||||
|
@ -5160,7 +5241,7 @@ ColorPaletteMorph.prototype.render = function (ctx) {
|
|||
var ext = this.extent(),
|
||||
x, y, h, l;
|
||||
|
||||
this.choice = new Color();
|
||||
this.choice = BLACK;
|
||||
for (x = 0; x <= ext.x; x += 1) {
|
||||
h = 360 * x / ext.x;
|
||||
for (y = 0; y <= ext.y; y += 1) {
|
||||
|
@ -5262,7 +5343,7 @@ GrayPaletteMorph.prototype.render = function (ctx) {
|
|||
var ext = this.extent(),
|
||||
gradient;
|
||||
|
||||
this.choice = new Color();
|
||||
this.choice = BLACK;
|
||||
gradient = ctx.createLinearGradient(0, 0, ext.x, ext.y);
|
||||
gradient.addColorStop(0, 'black');
|
||||
gradient.addColorStop(1, 'white');
|
||||
|
@ -5281,13 +5362,13 @@ ColorPickerMorph.uber = Morph.prototype;
|
|||
// ColorPickerMorph instance creation:
|
||||
|
||||
function ColorPickerMorph(defaultColor) {
|
||||
this.init(defaultColor || new Color(255, 255, 255));
|
||||
this.init(defaultColor || WHITE);
|
||||
}
|
||||
|
||||
ColorPickerMorph.prototype.init = function (defaultColor) {
|
||||
this.choice = defaultColor;
|
||||
ColorPickerMorph.uber.init.call(this);
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.setExtent(new Point(80, 80));
|
||||
};
|
||||
|
||||
|
@ -5723,7 +5804,7 @@ function BoxMorph(edge, border, borderColor) {
|
|||
BoxMorph.prototype.init = function (edge, border, borderColor) {
|
||||
this.edge = edge || 4;
|
||||
this.border = border || ((border === 0) ? 0 : 2);
|
||||
this.borderColor = borderColor || new Color();
|
||||
this.borderColor = borderColor || BLACK;
|
||||
BoxMorph.uber.init.call(this);
|
||||
};
|
||||
|
||||
|
@ -6947,18 +7028,24 @@ SliderMorph.prototype.fixLayout = function () {
|
|||
bh = Math.max(bw, Math.round(this.height() * this.ratio()));
|
||||
this.button.setExtent(new Point(bw, bh));
|
||||
posX = 1;
|
||||
posY = Math.min(
|
||||
Math.round((this.value - this.start) * this.unitSize()),
|
||||
this.height() - this.button.height()
|
||||
posY = Math.max(
|
||||
Math.min(
|
||||
Math.round((this.value - this.start) * this.unitSize()),
|
||||
this.height() - this.button.height()
|
||||
),
|
||||
0
|
||||
);
|
||||
} else {
|
||||
bh = this.height() - 2;
|
||||
bw = Math.max(bh, Math.round(this.width() * this.ratio()));
|
||||
this.button.setExtent(new Point(bw, bh));
|
||||
posY = 1;
|
||||
posX = Math.min(
|
||||
Math.round((this.value - this.start) * this.unitSize()),
|
||||
this.width() - this.button.width()
|
||||
posX = Math.max(
|
||||
Math.min(
|
||||
Math.round((this.value - this.start) * this.unitSize()),
|
||||
this.width() - this.button.width()
|
||||
),
|
||||
0
|
||||
);
|
||||
}
|
||||
this.button.setPosition(
|
||||
|
@ -7233,8 +7320,8 @@ MouseSensorMorph.prototype.init = function (edge, border, borderColor) {
|
|||
MouseSensorMorph.uber.init.call(this);
|
||||
this.edge = edge || 4;
|
||||
this.border = border || 2;
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.borderColor = borderColor || new Color();
|
||||
this.color = WHITE;
|
||||
this.borderColor = borderColor || BLACK;
|
||||
this.isTouched = false;
|
||||
this.upStep = 0.05;
|
||||
this.downStep = 0.02;
|
||||
|
@ -7393,7 +7480,7 @@ InspectorMorph.prototype.buildPanes = function () {
|
|||
this.label = new TextMorph(this.target.toString());
|
||||
this.label.fontSize = MorphicPreferences.menuFontSize;
|
||||
this.label.isBold = true;
|
||||
this.label.color = new Color(255, 255, 255);
|
||||
this.label.color = WHITE;
|
||||
this.add(this.label);
|
||||
|
||||
// properties list
|
||||
|
@ -7459,7 +7546,7 @@ InspectorMorph.prototype.buildPanes = function () {
|
|||
this.detail.acceptsDrops = false;
|
||||
this.detail.contents.acceptsDrops = false;
|
||||
this.detail.isTextLineWrapping = true;
|
||||
this.detail.color = new Color(255, 255, 255);
|
||||
this.detail.color = WHITE;
|
||||
this.detail.hBar.alpha = 0.6;
|
||||
this.detail.vBar.alpha = 0.6;
|
||||
ctrl = new TextMorph('');
|
||||
|
@ -7476,7 +7563,7 @@ InspectorMorph.prototype.buildPanes = function () {
|
|||
this.work.acceptsDrops = false;
|
||||
this.work.contents.acceptsDrops = false;
|
||||
this.work.isTextLineWrapping = true;
|
||||
this.work.color = new Color(255, 255, 255);
|
||||
this.work.color = WHITE;
|
||||
this.work.hBar.alpha = 0.6;
|
||||
this.work.vBar.alpha = 0.6;
|
||||
ev = new TextMorph('');
|
||||
|
@ -7897,7 +7984,7 @@ MenuMorph.prototype.createLabel = function () {
|
|||
'center'
|
||||
);
|
||||
text.alignment = 'center';
|
||||
text.color = new Color(255, 255, 255);
|
||||
text.color = WHITE;
|
||||
text.backgroundColor = this.borderColor;
|
||||
text.fixLayout();
|
||||
this.label = new BoxMorph(3, 0);
|
||||
|
@ -7924,7 +8011,7 @@ MenuMorph.prototype.createItems = function () {
|
|||
this.edge = MorphicPreferences.isFlat ? 0 : 5;
|
||||
this.border = MorphicPreferences.isFlat ? 1 : 2;
|
||||
}
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.borderColor = new Color(60, 60, 60);
|
||||
this.setExtent(new Point(0, 0));
|
||||
|
||||
|
@ -8136,6 +8223,7 @@ MenuMorph.prototype.closeSubmenu = function () {
|
|||
this.submenu.destroy();
|
||||
this.submenu = null;
|
||||
this.unselectAllItems();
|
||||
this.world.activeMenu = this;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8349,7 +8437,7 @@ StringMorph.prototype.init = function (
|
|||
this.enableLinks = false; // set to "true" if I can contain clickable URLs
|
||||
this.isNumeric = isNumeric || false;
|
||||
this.isPassword = false;
|
||||
this.shadowOffset = shadowOffset || new Point(0, 0);
|
||||
this.shadowOffset = shadowOffset || ZERO;
|
||||
this.shadowColor = shadowColor || null;
|
||||
this.isShowingBlanks = false;
|
||||
this.blanksColor = new Color(180, 140, 140);
|
||||
|
@ -8359,7 +8447,7 @@ StringMorph.prototype.init = function (
|
|||
this.currentlySelecting = false;
|
||||
this.startMark = 0;
|
||||
this.endMark = 0;
|
||||
this.markedTextColor = new Color(255, 255, 255);
|
||||
this.markedTextColor = WHITE;
|
||||
this.markedBackgoundColor = new Color(60, 60, 120);
|
||||
|
||||
// initialize inherited properties:
|
||||
|
@ -8405,7 +8493,7 @@ StringMorph.prototype.font = function () {
|
|||
StringMorph.prototype.fixLayout = function (justMe) {
|
||||
// determine my extent depending on my current settings
|
||||
var width,
|
||||
shadowOffset = this.shadowOffset || new Point(),
|
||||
shadowOffset = this.shadowOffset || ZERO,
|
||||
txt = this.isPassword ?
|
||||
this.password('*', this.text.length) : this.text;
|
||||
|
||||
|
@ -8431,7 +8519,7 @@ StringMorph.prototype.fixLayout = function (justMe) {
|
|||
|
||||
StringMorph.prototype.render = function (ctx) {
|
||||
var start, stop, i, p, c, x, y,
|
||||
shadowOffset = this.shadowOffset || new Point(),
|
||||
shadowOffset = this.shadowOffset || ZERO,
|
||||
txt = this.isPassword ?
|
||||
this.password('*', this.text.length) : this.text;
|
||||
|
||||
|
@ -8524,21 +8612,14 @@ StringMorph.prototype.slotPosition = function (slot) {
|
|||
// where the cursor should be placed
|
||||
var txt = this.isPassword ?
|
||||
this.password('*', this.text.length) : this.text,
|
||||
dest = Math.min(Math.max(slot, 0), txt.length),
|
||||
xOffset,
|
||||
x,
|
||||
y,
|
||||
idx;
|
||||
dest = Math.min(Math.max(slot, 0), txt.length);
|
||||
|
||||
xOffset = 0;
|
||||
this.measureCtx.font = this.font();
|
||||
for (idx = 0; idx < dest; idx += 1) {
|
||||
xOffset += this.measureCtx.measureText(txt[idx]).width;
|
||||
}
|
||||
this.pos = dest;
|
||||
x = this.left() + xOffset;
|
||||
y = this.top();
|
||||
return new Point(x, y);
|
||||
return new Point(
|
||||
this.left() + this.measureCtx.measureText(txt.slice(0, dest)).width,
|
||||
this.top()
|
||||
);
|
||||
};
|
||||
|
||||
StringMorph.prototype.slotAt = function (aPoint) {
|
||||
|
@ -9054,7 +9135,7 @@ TextMorph.prototype.init = function (
|
|||
this.isBold = bold || false;
|
||||
this.isItalic = italic || false;
|
||||
this.alignment = alignment || 'left';
|
||||
this.shadowOffset = shadowOffset || new Point(0, 0);
|
||||
this.shadowOffset = shadowOffset || ZERO;
|
||||
this.shadowColor = shadowColor || null;
|
||||
this.maxWidth = width || 0;
|
||||
this.maxLineWidth = 0;
|
||||
|
@ -9070,7 +9151,7 @@ TextMorph.prototype.init = function (
|
|||
this.currentlySelecting = false;
|
||||
this.startMark = 0;
|
||||
this.endMark = 0;
|
||||
this.markedTextColor = new Color(255, 255, 255);
|
||||
this.markedTextColor = WHITE;
|
||||
this.markedBackgoundColor = new Color(60, 60, 120);
|
||||
|
||||
// initialize inherited properties:
|
||||
|
@ -9276,26 +9357,18 @@ TextMorph.prototype.slotPosition = function (slot) {
|
|||
ctx = this.measureCtx,
|
||||
shadowHeight = Math.abs(this.shadowOffset.y),
|
||||
xOffset = 0,
|
||||
yOffset,
|
||||
x,
|
||||
y,
|
||||
idx;
|
||||
yOffset;
|
||||
|
||||
ctx.font = this.font();
|
||||
yOffset = colRow.y * (fontHeight(this.fontSize) + shadowHeight);
|
||||
for (idx = 0; idx < colRow.x; idx += 1) {
|
||||
xOffset += ctx.measureText(this.lines[colRow.y][idx]).width;
|
||||
}
|
||||
x = this.left() + xOffset;
|
||||
y = this.top() + yOffset;
|
||||
return new Point(x, y);
|
||||
xOffset = ctx.measureText(this.lines[colRow.y].slice(0, colRow.x)).width;
|
||||
return new Point(this.left() + xOffset, this.top() + yOffset);
|
||||
};
|
||||
|
||||
TextMorph.prototype.slotAt = function (aPoint) {
|
||||
// answer the slot (index) closest to the given point taking
|
||||
// in account how far from the middle of the character it is,
|
||||
// so the cursor can be moved accordingly
|
||||
|
||||
var charX,
|
||||
row = 0,
|
||||
col = 0,
|
||||
|
@ -9626,7 +9699,7 @@ TriggerMorph.prototype.init = function (
|
|||
TriggerMorph.uber.init.call(this);
|
||||
|
||||
// override inherited properites:
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.createLabel();
|
||||
};
|
||||
|
||||
|
@ -10673,7 +10746,7 @@ ListMorph.prototype.init = function (
|
|||
ListMorph.uber.init.call(this);
|
||||
|
||||
this.contents.acceptsDrops = false;
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.hBar.alpha = 0.6;
|
||||
this.vBar.alpha = 0.6;
|
||||
this.elements = elements || [];
|
||||
|
@ -10814,7 +10887,7 @@ StringFieldMorph.prototype.init = function (
|
|||
this.isNumeric = isNumeric || false;
|
||||
this.text = null;
|
||||
StringFieldMorph.uber.init.call(this);
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.isEditable = true;
|
||||
this.acceptsDrops = false;
|
||||
this.createText();
|
||||
|
@ -10994,7 +11067,7 @@ HandMorph.prototype.changed = function () {
|
|||
var b;
|
||||
if (this.world !== null) {
|
||||
b = this.cachedFullBounds || this.fullBounds();
|
||||
if (!b.extent().eq(new Point())) {
|
||||
if (!b.extent().eq(ZERO)) {
|
||||
this.world.broken.push(b.spread());
|
||||
}
|
||||
}
|
||||
|
@ -11835,6 +11908,7 @@ WorldMorph.prototype.initKeyboardHandler = function () {
|
|||
}
|
||||
kbd = document.createElement('textarea');
|
||||
kbd.setAttribute('id', 'morphic_keyboard');
|
||||
kbd.setAttribute('style', 'caret-color:transparent;');
|
||||
kbd.world = this;
|
||||
kbd.style.zIndex = -1;
|
||||
kbd.style.position = 'absolute';
|
||||
|
@ -11994,7 +12068,7 @@ WorldMorph.prototype.initEventListeners = function () {
|
|||
canvas.addEventListener(
|
||||
"touchmove",
|
||||
event => this.hand.processTouchMove(event),
|
||||
false
|
||||
{passive: true}
|
||||
);
|
||||
|
||||
canvas.addEventListener(
|
||||
|
@ -12404,6 +12478,12 @@ WorldMorph.prototype.edit = function (aStringOrTextMorph) {
|
|||
if (this.cursor) {
|
||||
this.cursor.destroy();
|
||||
}
|
||||
|
||||
// some magic we apparently need for Android
|
||||
this.worldCanvas.focus();
|
||||
this.keyboardHandler.focus();
|
||||
|
||||
// create a new cursor
|
||||
this.cursor = new CursorMorph(aStringOrTextMorph, this.keyboardHandler);
|
||||
this.keyboardFocus = this.cursor;
|
||||
aStringOrTextMorph.parent.add(this.cursor);
|
||||
|
|
292
src/morphic.txt
292
src/morphic.txt
|
@ -7,9 +7,9 @@
|
|||
written by Jens Mönig
|
||||
jens@moenig.org
|
||||
|
||||
Copyright (C) 2019 by Jens Mönig
|
||||
Copyright (C) 2010-2020 by Jens Mönig
|
||||
|
||||
this documentation last changed: October 22, 2019
|
||||
This documentation last changed: June 9, 2020
|
||||
|
||||
This file is part of Snap!.
|
||||
|
||||
|
@ -52,12 +52,18 @@
|
|||
(h) text editing events
|
||||
(4) stepping
|
||||
(5) creating new kinds of morphs
|
||||
(a) drawing the shape
|
||||
(b) determining extent and arranging submorphs
|
||||
(c) pixel-perfect pointing events
|
||||
(d) caching the shape
|
||||
(e) holes
|
||||
(f) updating
|
||||
(g) duplicating
|
||||
(6) development and user modes
|
||||
(7) turtle graphics
|
||||
(8) damage list housekeeping
|
||||
(9) supporting high-resolution "retina" screens
|
||||
(10 animations
|
||||
(11) minifying morphic.js
|
||||
(8) supporting high-resolution "retina" screens
|
||||
(9 animations
|
||||
(10) minifying morphic.js
|
||||
VIII. acknowledgements
|
||||
IX. contributors
|
||||
|
||||
|
@ -153,8 +159,7 @@
|
|||
III. yet to implement
|
||||
---------------------
|
||||
- keyboard support for scroll frames and lists
|
||||
- full keyboard support for menus (partial support exists)
|
||||
- virtual keyboard support for Android and IE
|
||||
- virtual keyboard support for Android
|
||||
|
||||
|
||||
IV. open issues
|
||||
|
@ -192,7 +197,7 @@
|
|||
* 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)
|
||||
redrawn at 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
|
||||
|
@ -201,7 +206,7 @@
|
|||
events)
|
||||
* a single text entry focus per World
|
||||
|
||||
In its current state morphic.js doesn't support Transforms (you
|
||||
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
|
||||
|
@ -233,11 +238,11 @@
|
|||
|
||||
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.
|
||||
and may also have a keyboard focus 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
|
||||
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.
|
||||
|
||||
|
@ -268,7 +273,6 @@
|
|||
|
||||
window.onload = function () {
|
||||
world = new WorldMorph(document.getElementById('world'));
|
||||
world.worldCanvas.focus();
|
||||
world.isDevMode = true;
|
||||
loop();
|
||||
};
|
||||
|
@ -281,7 +285,7 @@
|
|||
</head>
|
||||
<body style="margin: 0;">
|
||||
<canvas id="world" tabindex="1" width="800" height="600"
|
||||
style="position: absolute;" />
|
||||
style="position: absolute;"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -311,7 +315,7 @@
|
|||
<title>Morphic!</title>
|
||||
<script type="text/javascript" src="morphic.js"></script>
|
||||
<script type="text/javascript">
|
||||
var world1, world2;
|
||||
var world1, world2;
|
||||
|
||||
window.onload = function () {
|
||||
disableRetinaSupport();
|
||||
|
@ -323,7 +327,7 @@
|
|||
};
|
||||
|
||||
function loop() {
|
||||
requestAnimationFrame(loop);
|
||||
requestAnimationFrame(loop);
|
||||
world1.doOneCycle();
|
||||
world2.doOneCycle();
|
||||
}
|
||||
|
@ -331,9 +335,9 @@
|
|||
</head>
|
||||
<body>
|
||||
<p>first world:</p>
|
||||
<canvas id="world1" tabindex="1" width="600" height="400" />
|
||||
<canvas id="world1" tabindex="1" width="600" height="400"></canvas>
|
||||
<p>second world:</p>
|
||||
<canvas id="world2" tabindex="2" width="400" height="600" />
|
||||
<canvas id="world2" tabindex="2" width="400" height="600"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -364,7 +368,6 @@
|
|||
|
||||
worldCanvas = document.getElementById('world');
|
||||
world = new WorldMorph(worldCanvas);
|
||||
world.worldCanvas.focus();
|
||||
world.isDevMode = false;
|
||||
world.setColor(new Color());
|
||||
|
||||
|
@ -397,7 +400,7 @@
|
|||
</head>
|
||||
<body bgcolor='black' style="margin: 0;">
|
||||
<canvas id="world" width="800" height="600"
|
||||
style="position: absolute;" />
|
||||
style="position: absolute;"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -452,7 +455,7 @@
|
|||
events.
|
||||
|
||||
These system events are dispatched within the morphic World by the
|
||||
World's Hand and its keyboardReceiver (usually the active text
|
||||
World's Hand and its keyboardFocus (usually the active text
|
||||
cursor).
|
||||
|
||||
|
||||
|
@ -631,12 +634,12 @@
|
|||
droppedImage(aCanvas, name)
|
||||
droppedSVG(anImage, name)
|
||||
|
||||
events to interested Morphs at the mouse pointer. If you want you Morph
|
||||
events to interested Morphs at the mouse pointer. If you want your 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
|
||||
Morph's cachedImage 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
|
||||
|
@ -668,16 +671,16 @@
|
|||
(e) keyboard events
|
||||
-------------------
|
||||
The World dispatches the following key events to its active
|
||||
keyboardReceiver:
|
||||
keyboard focus:
|
||||
|
||||
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 morphs which acts as keyboard focus are
|
||||
CursorMorph - the basic text editing widget - and MenuMorph elements.
|
||||
If you wish to add keyboard support to your morph you need to add event
|
||||
handling methods for
|
||||
|
||||
processKeyPress(event)
|
||||
processKeyDown(event)
|
||||
|
@ -685,7 +688,7 @@
|
|||
|
||||
and activate them by assigning your morph to the World's
|
||||
|
||||
keyboardReceiver
|
||||
keyboardFocus
|
||||
|
||||
property.
|
||||
|
||||
|
@ -730,8 +733,7 @@
|
|||
MyMorph.prototype.reactToWorldResize = function (rect) {
|
||||
this.changed();
|
||||
this.bounds = rect.insetBy(10);
|
||||
this.drawNew();
|
||||
this.changed();
|
||||
this.rerender();
|
||||
};
|
||||
|
||||
|
||||
|
@ -805,17 +807,17 @@
|
|||
|
||||
reactToSliderEdit(StringOrTextMorph)
|
||||
|
||||
events is dispatched, allowing for "Bret-Victor" style "live coding"
|
||||
events is dispatched, allowing for "Bret-Victor" style "scrubbing"
|
||||
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
|
||||
change notifications to their direct parents whenever their contents
|
||||
changes. 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 only from instances of TextMorph
|
||||
fixLayout() - sent from instances of all Morphs, including StringMorphs
|
||||
|
||||
they are different so that Morphs which contain both multi-line and
|
||||
single-line text elements can hold them apart.
|
||||
|
@ -854,28 +856,156 @@
|
|||
--------------------------------
|
||||
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
|
||||
musical notes.
|
||||
|
||||
drawNew()
|
||||
When you create your own morphs, you'll want to think about how to
|
||||
graphically render it, how to determine its size and whether it needs
|
||||
to arrange any other parts ("submorphs). There are also ways to specify
|
||||
its collision detection behavior and define "untouchable" regions
|
||||
("holes").
|
||||
|
||||
|
||||
(a) drawing the shape
|
||||
---------------------
|
||||
For this you have to override the default
|
||||
|
||||
render(ctx)
|
||||
|
||||
method.
|
||||
|
||||
This method creates a new offscreen Canvas and stores it in
|
||||
the morph's
|
||||
This method draws the morph's shape using a given 2d graphics context.
|
||||
Note that any coordinates used in the render() method must be relative
|
||||
to the morph's own position, i.e. you don't need to worry about
|
||||
translating the shape yourself.
|
||||
|
||||
image
|
||||
You can use the following template for a start:
|
||||
|
||||
property.
|
||||
|
||||
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.render = function(ctx) {
|
||||
ctx.fillStyle = this.color.toString();
|
||||
ctx.fillRect(0, 0, this.width(), this.height());
|
||||
};
|
||||
|
||||
it renders the morph as a solid rectangle completely filling its
|
||||
area with its current color.
|
||||
|
||||
Notice how the coordinates for the fillRect() call are relative
|
||||
to the morph's own position: The rendered rectangle's origin is always
|
||||
located at (0, 0) regardless of the morph's actual position in the World.
|
||||
|
||||
|
||||
(b) determining extent and arranging submorphs
|
||||
----------------------------------------------
|
||||
If your new morph also needs to determine its extent and, e.g. to
|
||||
encompass one or several other morphs, or arrange the layout of its
|
||||
submorphs, make sure to also override the default
|
||||
|
||||
fixLayout()
|
||||
|
||||
method.
|
||||
|
||||
NOTE: If you need to set the morph's extent inside, in order to avoid
|
||||
infinite recursion instead of calling morph.setExtent() - which will
|
||||
in turn call morph.fixLayout() again - directly modify the morph's
|
||||
|
||||
bounds
|
||||
|
||||
property. Bounds is a rectable on which you can also use the same
|
||||
size-setters, e.g. by calling:
|
||||
|
||||
this.bounds.setExtent()
|
||||
|
||||
|
||||
(c) pixel-perfect pointing events
|
||||
---------------------------------
|
||||
In case your new morph needs to support pixel-perfect collision detection
|
||||
with other morphs or pointing devices such as the mouse or a stylus you
|
||||
can set the inherited attribute
|
||||
|
||||
isFreeForm = bool
|
||||
|
||||
to "true" (default is "false"). This makes sense the more your morph's
|
||||
visual shape diverges from a rectangle. For example, if you create a
|
||||
circular filled morph the default setting will register mouse-events
|
||||
anywhere within its bounding box, e.g. also in the transparent parts
|
||||
between the bounding box's corners outside of the circle's bounds.
|
||||
Instead you can specify your irregulary shaped morph to only register
|
||||
pointing events (mouse and touch) on solid, non-transparent parts.
|
||||
|
||||
Notice, however, that such pixel-perfect collision detection might
|
||||
strain processing resources, especially if applied liberally.
|
||||
|
||||
In order to mitigate unfavorable processor loads for pixel-perfect
|
||||
collision deteciton of irregularly shaped morphs there are two strategies
|
||||
to consider: Caching the shape and specifying "untouchable" regions.
|
||||
|
||||
|
||||
(d) caching the shape
|
||||
---------------------
|
||||
In case of pixel-perfect free-form collision detection it makes sense to
|
||||
cache your morph's current shape, so it doesn't have to be re-drawn onto a
|
||||
new Canvas element every time the mouse moves over its bounding box.
|
||||
For this you can set then inherited
|
||||
|
||||
isCachingImage = bool
|
||||
|
||||
attribute to "true" instead of the default "false" value. This will
|
||||
significantly speed up collision detection and smoothen animations that
|
||||
continuously perform collision detection. However, it will also consume
|
||||
more memory. Therefore it's best to use this setting with caution.
|
||||
|
||||
Snap! caches the shapes of sprites but not those of blocks. Instead it
|
||||
manages the insides of C- and E-shaped blocks through the morphic "holes"
|
||||
mechanism.
|
||||
|
||||
|
||||
(e) holes
|
||||
---------
|
||||
An alternative albeit not as precise and general way for handling
|
||||
irregularly shaped morphs with "untouchable" regions is to specify a set
|
||||
of rectangular areas in which pointing events (mouse or touch) are not
|
||||
registered.
|
||||
|
||||
By default the inherited
|
||||
|
||||
holes = []
|
||||
|
||||
property is an empty array. You can add one or more morphic Rectangle
|
||||
objects to this list, representing regions, in which occurring events will
|
||||
instead be passed on to the morph underneath.
|
||||
|
||||
Note that, same with the render() method, the coordinates of these
|
||||
rectangular holes must be specified relative to your morph's position.
|
||||
|
||||
If you specify holes you might find the need to adjust their layout
|
||||
depending on the layout of your morph. To accomplish this you can override
|
||||
the inherited
|
||||
|
||||
fixHolesLayout()
|
||||
|
||||
method.
|
||||
|
||||
|
||||
(f) updating
|
||||
------------
|
||||
One way for morphs to become alive is form them to literally "morph" their
|
||||
shape depending on whicher contest you wish them to react to. For example,
|
||||
you might want the user to interactively draw a shape using their fingers
|
||||
on a touch screen device, or you want the user to be able to "pinch" or
|
||||
otherwise distort a shape interactively. In all of these situations you'll
|
||||
want your morph to frequently rerender its shape.
|
||||
|
||||
You can accomplish this, by calling
|
||||
|
||||
rerender()
|
||||
|
||||
after every change to your morph's appearance that requires rerendering.
|
||||
|
||||
Such changes are usually only happening when the morph's dimensions or
|
||||
other visual properties - such as its color - changes.
|
||||
|
||||
|
||||
(g) duplicating
|
||||
---------------
|
||||
If your new morph stores or references to other morphs outside of
|
||||
the submorph tree in other properties, be sure to also override the
|
||||
default
|
||||
|
@ -941,7 +1071,7 @@
|
|||
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()"
|
||||
the trails morph (the pen's parent) performs a "render()"
|
||||
operation. If you want to create your own pen trails canvas, you
|
||||
may wish to modify its
|
||||
|
||||
|
@ -953,18 +1083,18 @@
|
|||
the following properties of PenMorph are relevant for turtle
|
||||
graphics:
|
||||
|
||||
color - a Color
|
||||
color - a Color
|
||||
size - line width of pen trails
|
||||
heading - degrees
|
||||
isDown - drawing state
|
||||
heading - degrees
|
||||
isDown - drawing state
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -990,47 +1120,7 @@
|
|||
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.
|
||||
|
||||
An effective strategy to cope with this is to use the inherited
|
||||
|
||||
trackChanges
|
||||
|
||||
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. As an example refer to the
|
||||
|
||||
moveBy()
|
||||
|
||||
method of HandMorph, and to the
|
||||
|
||||
fixLayout()
|
||||
|
||||
method of InspectorMorph, or the
|
||||
|
||||
startLayout()
|
||||
endLayout()
|
||||
|
||||
methods of SyntaxElementMorph in the Snap application.
|
||||
|
||||
|
||||
(9) supporting high-resolution "retina" screens
|
||||
(8) supporting high-resolution "retina" screens
|
||||
-----------------------------------------------
|
||||
By default retina support gets installed when Morphic.js loads. There
|
||||
are two global functions that let you test for retina availability:
|
||||
|
@ -1076,7 +1166,7 @@
|
|||
stage (high-resolution) into a sprite-costume (normal resolution).
|
||||
|
||||
|
||||
(10) animations
|
||||
(9) animations
|
||||
---------------
|
||||
Animations handle gradual transitions between one state and another over a
|
||||
period of time. Transition effects can be specified using easing functions.
|
||||
|
@ -1106,7 +1196,7 @@
|
|||
are implemented.
|
||||
|
||||
|
||||
(11) minifying morphic.js
|
||||
(10) 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
|
||||
|
|
128
src/objects.js
128
src/objects.js
|
@ -74,7 +74,7 @@ ThreadManager, VariableFrame, detect, BlockMorph, BoxMorph, Color, Animation,
|
|||
CommandBlockMorph, FrameMorph, HatBlockMorph, MenuMorph, Morph, MultiArgMorph,
|
||||
Point, ReporterBlockMorph, ScriptsMorph, StringMorph, SyntaxElementMorph, nop,
|
||||
TextMorph, contains, degrees, detect, newCanvas, radians, Array, CursorMorph,
|
||||
Date, FrameMorph, Math, MenuMorph, Morph, invoke, MorphicPreferences,
|
||||
Date, FrameMorph, Math, MenuMorph, Morph, invoke, MorphicPreferences, WHITE,
|
||||
Object, PenMorph, Point, Rectangle, ScrollFrameMorph, SliderMorph, String,
|
||||
StringMorph, TextMorph, contains, copy, degrees, detect, document, isNaN,
|
||||
isString, newCanvas, nop, parseFloat, radians, window, modules, IDE_Morph,
|
||||
|
@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph,
|
|||
localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph,
|
||||
HandleMorph, AlignmentMorph, Process, XML_Element, WorldMap, copyCanvas*/
|
||||
|
||||
modules.objects = '2020-May-06';
|
||||
modules.objects = '2020-July-01';
|
||||
|
||||
var SpriteMorph;
|
||||
var StageMorph;
|
||||
|
@ -172,7 +172,7 @@ SpriteMorph.prototype.useFlatLineEnds = false;
|
|||
SpriteMorph.prototype.highlightColor = new Color(250, 200, 130);
|
||||
SpriteMorph.prototype.highlightBorder = 8;
|
||||
|
||||
SpriteMorph.prototype.bubbleColor = new Color(255, 255, 255);
|
||||
SpriteMorph.prototype.bubbleColor = WHITE;
|
||||
SpriteMorph.prototype.bubbleFontSize = 14;
|
||||
SpriteMorph.prototype.bubbleFontIsBold = true;
|
||||
SpriteMorph.prototype.bubbleCorner = 10;
|
||||
|
@ -1187,7 +1187,7 @@ SpriteMorph.prototype.initBlocks = function () {
|
|||
spec: 'split %s by %delim',
|
||||
defaults: [localize('hello') + ' ' + localize('world'), " "]
|
||||
},
|
||||
reportJSFunction: { // experimental
|
||||
reportJSFunction: {
|
||||
type: 'reporter',
|
||||
category: 'operators',
|
||||
spec: 'JavaScript function ( %mult%s ) { %code }'
|
||||
|
@ -1525,12 +1525,23 @@ SpriteMorph.prototype.initBlockMigrations = function () {
|
|||
SpriteMorph.prototype.initBlockMigrations();
|
||||
|
||||
SpriteMorph.prototype.blockAlternatives = {
|
||||
// structure:
|
||||
// selector: [ersatz, ...]
|
||||
// ersatz can also be a 2-item array: [selector, input-offset]
|
||||
|
||||
// motion:
|
||||
forward: ['changeXPosition', 'changeYPosition'],
|
||||
turn: ['turnLeft'],
|
||||
turnLeft: ['turn'],
|
||||
changeXPosition: ['changeYPosition', 'setXPosition', 'setYPosition'],
|
||||
doFaceTowards: ['doGotoObject'],
|
||||
gotoXY: [['doGlide', 1]],
|
||||
doGotoObject: ['doFaceTowards'],
|
||||
doGlide: [['gotoXY', -1]],
|
||||
changeXPosition: ['changeYPosition', 'setXPosition', 'setYPosition',
|
||||
'forward'],
|
||||
setXPosition: ['setYPosition', 'changeXPosition', 'changeYPosition'],
|
||||
changeYPosition: ['changeXPosition', 'setYPosition', 'setXPosition'],
|
||||
changeYPosition: ['changeXPosition', 'setYPosition', 'setXPosition',
|
||||
'forward'],
|
||||
setYPosition: ['setXPosition', 'changeYPosition', 'changeXPosition'],
|
||||
xPosition: ['yPosition'],
|
||||
yPosition: ['xPosition'],
|
||||
|
@ -1551,6 +1562,8 @@ SpriteMorph.prototype.blockAlternatives = {
|
|||
playSound: ['doPlaySoundUntilDone', 'doPlaySoundAtRate'],
|
||||
doPlaySoundUntilDone: ['playSound', 'doPlaySoundAtRate'],
|
||||
doPlaySoundAtRate: ['playSound', 'doPlaySoundUntilDone'],
|
||||
doPlayNote: [['doRest', -1]],
|
||||
doRest: [['doPlayNote', 1]],
|
||||
doChangeTempo: ['doSetTempo'],
|
||||
doSetTempo: ['doChangeTempo'],
|
||||
setVolume: ['changeVolume'],
|
||||
|
@ -1577,12 +1590,20 @@ SpriteMorph.prototype.blockAlternatives = {
|
|||
setSize: ['changeSize'],
|
||||
|
||||
// control:
|
||||
doBroadcast: ['doBroadcastAndWait'],
|
||||
doBroadcastAndWait: ['doBroadcast'],
|
||||
doBroadcast: ['doBroadcastAndWait', 'doSend'],
|
||||
doBroadcastAndWait: ['doBroadcast', 'doSend'],
|
||||
doSend: ['doBroadcast', 'doBroadcastAndWait'],
|
||||
doIf: ['doIfElse', 'doUntil'],
|
||||
doIfElse: ['doIf', 'doUntil'],
|
||||
doRepeat: ['doUntil'],
|
||||
doUntil: ['doRepeat', 'doIf'],
|
||||
doRepeat: ['doUntil', ['doForever', -1], ['doFor', 2], ['doForEach', 1]],
|
||||
doUntil: ['doRepeat', 'doIf', ['doForever', -1], ['doFor', 2],
|
||||
['doForEach', 1]],
|
||||
doForever: [['doUntil', 1], ['doRepeat', 1], ['doFor', 3],
|
||||
['doForEach', 2]],
|
||||
doFor: [['doForever', -3], ['doRepeat', -2], ['doUntil', -2],
|
||||
['doForEach', -1]],
|
||||
// doRun: ['fork'],
|
||||
// fork: ['doRun'],
|
||||
|
||||
// sensing:
|
||||
doAsk: ['bubble', 'doThink', 'doSayFor', 'doThinkFor'],
|
||||
|
@ -1619,7 +1640,9 @@ SpriteMorph.prototype.blockAlternatives = {
|
|||
// lists - HOFs
|
||||
reportMap: ['reportKeep', 'reportFindFirst'],
|
||||
reportKeep: ['reportFindFirst', 'reportMap'],
|
||||
reportFindFirst: ['reportKeep', 'reportMap']
|
||||
reportFindFirst: ['reportKeep', 'reportMap'],
|
||||
doForEach: [['doFor', 1], ['doForever', -2], ['doRepeat', -1],
|
||||
['doUntil', -1]]
|
||||
};
|
||||
|
||||
// SpriteMorph instance creation
|
||||
|
@ -3152,8 +3175,7 @@ SpriteMorph.prototype.searchBlocks = function (
|
|||
if (focus) {focus.destroy(); }
|
||||
if (!selection || !scriptFocus) {return; }
|
||||
focus = selection.outline(
|
||||
MorphicPreferences.isFlat ? new Color(150, 200, 255)
|
||||
: new Color(255, 255, 255),
|
||||
MorphicPreferences.isFlat ? new Color(150, 200, 255) : WHITE,
|
||||
2
|
||||
);
|
||||
searchPane.contents.add(focus);
|
||||
|
@ -4089,6 +4111,9 @@ SpriteMorph.prototype.release = function () {
|
|||
) || this.stage;
|
||||
}
|
||||
ide.selectSprite(ide.currentSprite);
|
||||
if (ide.isAppMode) {
|
||||
ide.toggleAppMode(true);
|
||||
}
|
||||
};
|
||||
|
||||
// SpriteMorph deleting
|
||||
|
@ -5967,6 +5992,7 @@ SpriteMorph.prototype.findVariableWatcher = function (varName) {
|
|||
|
||||
SpriteMorph.prototype.toggleVariableWatcher = function (varName, isGlobal) {
|
||||
var stage = this.parentThatIsA(StageMorph),
|
||||
ide = this.parentThatIsA(IDE_Morph),
|
||||
globals = this.globalVariables(),
|
||||
watcher,
|
||||
others;
|
||||
|
@ -5974,6 +6000,9 @@ SpriteMorph.prototype.toggleVariableWatcher = function (varName, isGlobal) {
|
|||
if (stage === null) {
|
||||
return null;
|
||||
}
|
||||
if (isNil(isGlobal)) {
|
||||
isGlobal = contains(globals.names(), varName);
|
||||
}
|
||||
watcher = this.findVariableWatcher(varName);
|
||||
if (watcher !== null) {
|
||||
if (watcher.isVisible) {
|
||||
|
@ -5983,13 +6012,14 @@ SpriteMorph.prototype.toggleVariableWatcher = function (varName, isGlobal) {
|
|||
watcher.fixLayout(); // re-hide hidden parts
|
||||
watcher.keepWithin(stage);
|
||||
}
|
||||
if (isGlobal) {
|
||||
ide.flushBlocksCache('variables');
|
||||
ide.refreshPalette();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if no watcher exists, create a new one
|
||||
if (isNil(isGlobal)) {
|
||||
isGlobal = contains(globals.names(), varName);
|
||||
}
|
||||
watcher = new WatcherMorph(
|
||||
varName,
|
||||
this.blockColor.variables,
|
||||
|
@ -6037,6 +6067,7 @@ SpriteMorph.prototype.deleteVariableWatcher = function (varName) {
|
|||
|
||||
SpriteMorph.prototype.toggleWatcher = function (selector, label, color) {
|
||||
var stage = this.parentThatIsA(StageMorph),
|
||||
ide = this.parentThatIsA(IDE_Morph),
|
||||
watcher,
|
||||
others;
|
||||
if (!stage) { return; }
|
||||
|
@ -6049,6 +6080,10 @@ SpriteMorph.prototype.toggleWatcher = function (selector, label, color) {
|
|||
watcher.fixLayout(); // re-hide hidden parts
|
||||
watcher.keepWithin(stage);
|
||||
}
|
||||
if (watcher.isGlobal(selector)) {
|
||||
ide.flushBlocksCache();
|
||||
ide.refreshPalette();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6068,6 +6103,10 @@ SpriteMorph.prototype.toggleWatcher = function (selector, label, color) {
|
|||
watcher.fixLayout();
|
||||
watcher.keepWithin(stage);
|
||||
watcher.changed();
|
||||
if (watcher.isGlobal(selector)) {
|
||||
ide.flushBlocksCache();
|
||||
ide.refreshPalette();
|
||||
}
|
||||
};
|
||||
|
||||
SpriteMorph.prototype.showingWatcher = function (selector) {
|
||||
|
@ -6794,17 +6833,13 @@ SpriteMorph.prototype.hasSpriteVariable = function (varName) {
|
|||
};
|
||||
|
||||
SpriteMorph.prototype.allLocalVariableNames = function (sorted) {
|
||||
var exceptGlobals = this.globalVariables(),
|
||||
globalNames = exceptGlobals.names(),
|
||||
data;
|
||||
var data;
|
||||
|
||||
function alphabetically(x, y) {
|
||||
return x.toLowerCase() < y.toLowerCase() ? -1 : 1;
|
||||
}
|
||||
|
||||
data = this.variables.allNames(exceptGlobals).filter(each =>
|
||||
!contains(globalNames, each)
|
||||
);
|
||||
data = this.variables.names();
|
||||
if (sorted) {
|
||||
data.sort(alphabetically);
|
||||
}
|
||||
|
@ -7156,7 +7191,7 @@ SpriteMorph.prototype.highlight = function (color, border) {
|
|||
highlight.cachedImage = this.highlightImage(color, border);
|
||||
ctx = highlight.cachedImage.getContext('2d');
|
||||
ctx.drawImage(
|
||||
this.highlightImage(new Color(255, 255, 255), 4),
|
||||
this.highlightImage(WHITE, 4),
|
||||
border - 4,
|
||||
border - 4
|
||||
);
|
||||
|
@ -7166,7 +7201,7 @@ SpriteMorph.prototype.highlight = function (color, border) {
|
|||
border - 2
|
||||
);
|
||||
ctx.drawImage(
|
||||
this.highlightImage(new Color(255, 255, 255), 1),
|
||||
this.highlightImage(WHITE, 1),
|
||||
border - 1,
|
||||
border - 1
|
||||
);
|
||||
|
@ -8685,7 +8720,7 @@ StageMorph.prototype.userMenu = function () {
|
|||
menu.addItem(
|
||||
"pic...",
|
||||
() => ide.saveCanvasAs(this.fullImage(), this.name),
|
||||
'open a new window\nwith a picture of the stage'
|
||||
'save a picture\nof the stage'
|
||||
);
|
||||
menu.addLine();
|
||||
menu.addItem(
|
||||
|
@ -9507,10 +9542,10 @@ SpriteBubbleMorph.prototype.fixLayout = function () {
|
|||
|
||||
// Costume instance creation
|
||||
|
||||
function Costume(canvas, name, rotationCenter) {
|
||||
function Costume(canvas, name, rotationCenter, noFit) {
|
||||
this.contents = canvas ? normalizeCanvas(canvas, true)
|
||||
: newCanvas(null, true);
|
||||
this.shrinkToFit(this.maxExtent());
|
||||
if (!noFit) {this.shrinkToFit(this.maxExtent()); }
|
||||
this.name = name || null;
|
||||
this.rotationCenter = rotationCenter || this.center();
|
||||
this.version = Date.now(); // for observer optimization
|
||||
|
@ -9699,7 +9734,8 @@ Costume.prototype.stretched = function (w, h) {
|
|||
stretched = new Costume(
|
||||
canvas,
|
||||
this.name,
|
||||
center
|
||||
center,
|
||||
true
|
||||
);
|
||||
return stretched;
|
||||
};
|
||||
|
@ -9755,7 +9791,7 @@ Costume.prototype.editRotationPointOnly = function (aWorld) {
|
|||
null,
|
||||
null,
|
||||
new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
);
|
||||
|
||||
dialog.labelString = 'Costume Editor';
|
||||
|
@ -9998,7 +10034,7 @@ function CostumeEditorMorph(costume) {
|
|||
CostumeEditorMorph.prototype.init = function (costume) {
|
||||
this.costume = costume || new Costume();
|
||||
this.rotationCenter = this.costume.rotationCenter.copy();
|
||||
this.margin = new Point(0, 0);
|
||||
this.margin = ZERO;
|
||||
CostumeEditorMorph.uber.init.call(this);
|
||||
};
|
||||
|
||||
|
@ -10609,8 +10645,8 @@ CellMorph.prototype.init = function (contents, color, idx, parentCell) {
|
|||
CellMorph.uber.init.call(
|
||||
this,
|
||||
SyntaxElementMorph.prototype.corner,
|
||||
1.000001, // shadow bug in Chrome,
|
||||
new Color(255, 255, 255)
|
||||
1,
|
||||
WHITE
|
||||
);
|
||||
this.color = color || new Color(255, 140, 0);
|
||||
this.isBig = false;
|
||||
|
@ -10744,7 +10780,7 @@ CellMorph.prototype.createContents = function () {
|
|||
this.contentsMorph.isEditable = true;
|
||||
this.contentsMorph.enableSelecting();
|
||||
}
|
||||
this.contentsMorph.setColor(new Color(255, 255, 255));
|
||||
this.contentsMorph.setColor(WHITE);
|
||||
} else if (typeof this.contents === 'boolean') {
|
||||
img = SpriteMorph.prototype.booleanMorph.call(
|
||||
null,
|
||||
|
@ -10795,7 +10831,7 @@ CellMorph.prototype.createContents = function () {
|
|||
true, // italic
|
||||
'center'
|
||||
);
|
||||
this.contentsMorph.setColor(new Color(255, 255, 255));
|
||||
this.contentsMorph.setColor(WHITE);
|
||||
} else {
|
||||
this.contentsMorph = new ListWatcherMorph(
|
||||
this.contents,
|
||||
|
@ -10817,7 +10853,7 @@ CellMorph.prototype.createContents = function () {
|
|||
this.contentsMorph.isEditable = true;
|
||||
this.contentsMorph.enableSelecting();
|
||||
}
|
||||
this.contentsMorph.setColor(new Color(255, 255, 255));
|
||||
this.contentsMorph.setColor(WHITE);
|
||||
}
|
||||
this.add(this.contentsMorph);
|
||||
}
|
||||
|
@ -10926,7 +10962,10 @@ CellMorph.prototype.reactToEdit = function (textMorph) {
|
|||
if (!isNil(this.idx)) {
|
||||
listWatcher = this.parentThatIsA(ListWatcherMorph);
|
||||
if (listWatcher) {
|
||||
listWatcher.list.put(textMorph.text, this.idx);
|
||||
listWatcher.list.put(
|
||||
textMorph.text,
|
||||
this.idx
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -11182,7 +11221,7 @@ WatcherMorph.prototype.fixLayout = function () {
|
|||
false,
|
||||
false,
|
||||
MorphicPreferences.isFlat ? new Point() : new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
);
|
||||
this.add(this.labelMorph);
|
||||
}
|
||||
|
@ -11469,6 +11508,19 @@ WatcherMorph.prototype.userMenu = function () {
|
|||
);
|
||||
}
|
||||
);
|
||||
if (this.currentValue.canBeJSON()) {
|
||||
menu.addItem(
|
||||
'blockify',
|
||||
() => {
|
||||
var world = ide.world();
|
||||
this.currentValue.blockify().pickUp(world);
|
||||
world.hand.grabOrigin = {
|
||||
origin: ide.palette,
|
||||
position: ide.palette.center()
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
} else if (this.currentValue instanceof List &&
|
||||
this.currentValue.canBeJSON()) {
|
||||
menu.addItem(
|
||||
|
@ -11740,7 +11792,7 @@ StagePrompterMorph.prototype.init = function (question) {
|
|||
);
|
||||
|
||||
// override inherited behavior
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
if (this.label) {this.add(this.label); }
|
||||
this.add(this.inputField);
|
||||
this.add(this.button);
|
||||
|
|
|
@ -69,7 +69,8 @@
|
|||
Jan 22 - floodfill alpha tweak (Bernat)
|
||||
Mar 19 - vector paint editor (Bernat)
|
||||
|
||||
2020 Apr 14 - partial Morphic2 migration (Jens)
|
||||
2020 Apr 14 - Morphic2 migration (Jens)
|
||||
2020 May 17 - Pipette alpha fix (Joan)
|
||||
*/
|
||||
|
||||
/*global Point, Rectangle, DialogBoxMorph, AlignmentMorph, PushButtonMorph,
|
||||
|
@ -80,7 +81,7 @@ StageMorph, isNil, SVG_Costume*/
|
|||
|
||||
// Global stuff ////////////////////////////////////////////////////////
|
||||
|
||||
modules.paint = '2020-April-14';
|
||||
modules.paint = '2020-May-17';
|
||||
|
||||
// Declarations
|
||||
|
||||
|
@ -511,7 +512,7 @@ PaintEditorMorph.prototype.getUserColor = function () {
|
|||
// needed for retina-display support
|
||||
return;
|
||||
}
|
||||
color.a = 255;
|
||||
color.a = 1;
|
||||
myself.propertiesControls.colorpicker.action(color);
|
||||
};
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ normalizeCanvas, contains*/
|
|||
|
||||
// Global stuff ////////////////////////////////////////////////////////
|
||||
|
||||
modules.store = '2020-May-13';
|
||||
modules.store = '2020-May-18';
|
||||
|
||||
|
||||
// XML_Serializer ///////////////////////////////////////////////////////
|
||||
|
@ -247,7 +247,7 @@ SnapSerializer.uber = XML_Serializer.prototype;
|
|||
|
||||
// SnapSerializer constants:
|
||||
|
||||
SnapSerializer.prototype.app = 'Snap! 6.0 alpha, https://snap.berkeley.edu';
|
||||
SnapSerializer.prototype.app = 'Snap! 6.0 beta, https://snap.berkeley.edu';
|
||||
|
||||
SnapSerializer.prototype.thumbnailSize = new Point(160, 120);
|
||||
|
||||
|
|
300
src/symbols.js
300
src/symbols.js
|
@ -37,11 +37,11 @@
|
|||
|
||||
*/
|
||||
|
||||
/*global modules, Morph, Point, radians, Color, ZERO*/
|
||||
/*global modules, Morph, Point, radians, ZERO, BLACK*/
|
||||
|
||||
// Global stuff ////////////////////////////////////////////////////////
|
||||
|
||||
modules.symbols = '2020-May-06';
|
||||
modules.symbols = '2020-July-01';
|
||||
|
||||
var SymbolMorph;
|
||||
|
||||
|
@ -70,6 +70,7 @@ SymbolMorph.prototype.names = [
|
|||
'pointRight',
|
||||
'stepForward',
|
||||
'gears',
|
||||
'gearBig',
|
||||
'file',
|
||||
'fullScreen',
|
||||
'normalScreen',
|
||||
|
@ -86,10 +87,13 @@ SymbolMorph.prototype.names = [
|
|||
'cloudOutline',
|
||||
'turnRight',
|
||||
'turnLeft',
|
||||
'turnAround',
|
||||
'storage',
|
||||
'poster',
|
||||
'flash',
|
||||
'brush',
|
||||
'tick',
|
||||
'checkedBox',
|
||||
'rectangle',
|
||||
'rectangleSolid',
|
||||
'circle',
|
||||
|
@ -108,12 +112,18 @@ SymbolMorph.prototype.names = [
|
|||
'turnForward',
|
||||
'arrowUp',
|
||||
'arrowUpOutline',
|
||||
'arrowUpThin',
|
||||
'arrowUpDownThin',
|
||||
'arrowLeft',
|
||||
'arrowLeftOutline',
|
||||
'arrowLeftThin',
|
||||
'arrowLeftRightThin',
|
||||
'arrowDown',
|
||||
'arrowDownOutline',
|
||||
'arrowDownThin',
|
||||
'arrowRight',
|
||||
'arrowRightOutline',
|
||||
'arrowRightThin',
|
||||
'robot',
|
||||
'magnifyingGlass',
|
||||
'magnifierOutline',
|
||||
|
@ -127,6 +137,7 @@ SymbolMorph.prototype.names = [
|
|||
'keyboard',
|
||||
'keyboardFilled',
|
||||
'globe',
|
||||
'globeBig',
|
||||
'list'
|
||||
];
|
||||
|
||||
|
@ -150,7 +161,7 @@ SymbolMorph.prototype.init = function (
|
|||
this.shadowOffset = shadowOffset || ZERO;
|
||||
this.shadowColor = shadowColor || null;
|
||||
SymbolMorph.uber.init.call(this);
|
||||
this.color = color || new Color(0, 0, 0);
|
||||
this.color = color || BLACK;
|
||||
this.fixLayout();
|
||||
this.rerender();
|
||||
};
|
||||
|
@ -230,6 +241,9 @@ SymbolMorph.prototype.renderShape = function (ctx, aColor) {
|
|||
case 'gears':
|
||||
this.renderSymbolGears(ctx, aColor);
|
||||
break;
|
||||
case 'gearBig':
|
||||
this.renderSymbolGearBig(ctx, aColor);
|
||||
break;
|
||||
case 'file':
|
||||
this.renderSymbolFile(ctx, aColor);
|
||||
break;
|
||||
|
@ -278,6 +292,9 @@ SymbolMorph.prototype.renderShape = function (ctx, aColor) {
|
|||
case 'turnLeft':
|
||||
this.renderSymbolTurnLeft(ctx, aColor);
|
||||
break;
|
||||
case 'turnAround':
|
||||
this.renderSymbolTurnAround(ctx, aColor);
|
||||
break;
|
||||
case 'storage':
|
||||
this.renderSymbolStorage(ctx, aColor);
|
||||
break;
|
||||
|
@ -290,6 +307,12 @@ SymbolMorph.prototype.renderShape = function (ctx, aColor) {
|
|||
case 'brush':
|
||||
this.renderSymbolBrush(ctx, aColor);
|
||||
break;
|
||||
case 'tick':
|
||||
this.renderSymbolTick(ctx, aColor);
|
||||
break;
|
||||
case 'checkedBox':
|
||||
this.renderSymbolCheckedBox(ctx, aColor);
|
||||
break;
|
||||
case 'rectangle':
|
||||
this.renderSymbolRectangle(ctx, aColor);
|
||||
break;
|
||||
|
@ -344,24 +367,42 @@ SymbolMorph.prototype.renderShape = function (ctx, aColor) {
|
|||
case 'arrowUpOutline':
|
||||
this.renderSymbolArrowUpOutline(ctx, aColor);
|
||||
break;
|
||||
case 'arrowUpThin':
|
||||
this.renderSymbolArrowUpThin(ctx, aColor);
|
||||
break;
|
||||
case 'arrowUpDownThin':
|
||||
this.renderSymbolArrowUpDownThin(ctx, aColor);
|
||||
break;
|
||||
case 'arrowLeft':
|
||||
this.renderSymbolArrowLeft(ctx, aColor);
|
||||
break;
|
||||
case 'arrowLeftOutline':
|
||||
this.renderSymbolArrowLeftOutline(ctx, aColor);
|
||||
break;
|
||||
case 'arrowLeftThin':
|
||||
this.renderSymbolArrowLeftThin(ctx, aColor);
|
||||
break;
|
||||
case 'arrowLeftRightThin':
|
||||
this.renderSymbolArrowLeftRightThin(ctx, aColor);
|
||||
break;
|
||||
case 'arrowDown':
|
||||
this.renderSymbolArrowDown(ctx, aColor);
|
||||
break;
|
||||
case 'arrowDownOutline':
|
||||
this.renderSymbolArrowDownOutline(ctx, aColor);
|
||||
break;
|
||||
case 'arrowDownThin':
|
||||
this.renderSymbolArrowDownThin(ctx, aColor);
|
||||
break;
|
||||
case 'arrowRight':
|
||||
this.renderSymbolArrowRight(ctx, aColor);
|
||||
break;
|
||||
case 'arrowRightOutline':
|
||||
this.renderSymbolArrowRightOutline(ctx, aColor);
|
||||
break;
|
||||
case 'arrowRightThin':
|
||||
this.renderSymbolArrowRightThin(ctx, aColor);
|
||||
break;
|
||||
case 'robot':
|
||||
this.renderSymbolRobot(ctx, aColor);
|
||||
break;
|
||||
|
@ -401,6 +442,9 @@ SymbolMorph.prototype.renderShape = function (ctx, aColor) {
|
|||
case 'globe':
|
||||
this.renderSymbolGlobe(ctx, aColor);
|
||||
break;
|
||||
case 'globeBig':
|
||||
this.renderSymbolGlobeBig(ctx, aColor);
|
||||
break;
|
||||
case 'list':
|
||||
this.renderSymbolList(ctx, aColor);
|
||||
break;
|
||||
|
@ -491,21 +535,98 @@ SymbolMorph.prototype.renderSymbolGears = function (ctx, color) {
|
|||
// draw gears
|
||||
var w = this.symbolWidth(),
|
||||
r = w / 2,
|
||||
e = w / 6;
|
||||
spikes = 8,
|
||||
off = 8,
|
||||
shift = 10,
|
||||
angle, turn, i;
|
||||
|
||||
ctx.strokeStyle = color.toString();
|
||||
ctx.lineWidth = w / 7;
|
||||
ctx.fillStyle = color.toString();
|
||||
ctx.beginPath();
|
||||
ctx.arc(r, r, e * 1.5, radians(0), radians(360), false);
|
||||
ctx.moveTo(0, r);
|
||||
|
||||
// draw the spiked outline
|
||||
ctx.moveTo(w, r);
|
||||
angle = 360 / spikes;
|
||||
turn = angle * 0.5;
|
||||
for (i = 0; i < spikes; i += 1) {
|
||||
ctx.arc(
|
||||
r,
|
||||
r,
|
||||
r,
|
||||
radians(i * angle + turn),
|
||||
radians(i * angle + off + turn)
|
||||
);
|
||||
ctx.arc(
|
||||
r,
|
||||
r,
|
||||
r * 0.7,
|
||||
radians(i * angle - shift + angle * 0.5 + turn),
|
||||
radians(i * angle + shift + angle * 0.5 + turn)
|
||||
);
|
||||
ctx.arc(
|
||||
r,
|
||||
r,
|
||||
r,
|
||||
radians((i + 1) * angle - off + turn),
|
||||
radians((i + 1) * angle + turn)
|
||||
);
|
||||
}
|
||||
ctx.lineTo(w, r);
|
||||
ctx.moveTo(r, 0);
|
||||
ctx.lineTo(r, w);
|
||||
ctx.moveTo(e, e);
|
||||
ctx.lineTo(w - e, w - e);
|
||||
ctx.moveTo(w - e, e);
|
||||
ctx.lineTo(e, w - e);
|
||||
ctx.stroke();
|
||||
|
||||
// draw the hole in the middle
|
||||
ctx.arc(r, r, r * 0.3, radians(0), radians(360));
|
||||
|
||||
// fill
|
||||
ctx.clip('evenodd');
|
||||
ctx.fillRect(0, 0, w, w);
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolGearBig = function (ctx, color) {
|
||||
// draw a large gear
|
||||
var w = this.symbolWidth(),
|
||||
r = w / 2,
|
||||
spikes = 10,
|
||||
off = 7,
|
||||
shift = 8,
|
||||
angle, i;
|
||||
|
||||
ctx.fillStyle = color.toString();
|
||||
ctx.beginPath();
|
||||
|
||||
// draw the spiked outline
|
||||
ctx.moveTo(w, r);
|
||||
angle = 360 / spikes;
|
||||
for (i = 0; i < spikes; i += 1) {
|
||||
ctx.arc(
|
||||
r,
|
||||
r,
|
||||
r,
|
||||
radians(i * angle),
|
||||
radians(i * angle + off)
|
||||
);
|
||||
ctx.arc(
|
||||
r,
|
||||
r,
|
||||
r * 0.8,
|
||||
radians(i * angle - shift + angle * 0.5),
|
||||
radians(i * angle + shift + angle * 0.5)
|
||||
);
|
||||
ctx.arc(
|
||||
r,
|
||||
r,
|
||||
r,
|
||||
radians((i + 1) * angle - off),
|
||||
radians((i + 1) * angle)
|
||||
);
|
||||
}
|
||||
ctx.lineTo(w, r);
|
||||
|
||||
// draw the holes in the middle
|
||||
ctx.arc(r, r, r * 0.6, radians(0), radians(360));
|
||||
ctx.arc(r, r, r * 0.2, radians(0), radians(360));
|
||||
|
||||
// fill
|
||||
ctx.clip('evenodd');
|
||||
ctx.fillRect(0, 0, w, w);
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolFile = function (ctx, color) {
|
||||
|
@ -838,6 +959,26 @@ SymbolMorph.prototype.renderSymbolTurnLeft = function (ctx, color) {
|
|||
ctx.fill();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolTurnAround = function (ctx, color) {
|
||||
// draw a right-around-turning arrow
|
||||
var w = this.symbolWidth(),
|
||||
l = Math.max(w / 10, 1),
|
||||
r = w / 2;
|
||||
|
||||
ctx.lineWidth = l;
|
||||
ctx.strokeStyle = color.toString();
|
||||
ctx.beginPath();
|
||||
ctx.arc(r, r, r - l / 2, radians(-45), radians(225), false);
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = color.toString();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, r * 0.1);
|
||||
ctx.lineTo(r * 0.8, 0);
|
||||
ctx.lineTo(r * 0.7, r * 0.7);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolStorage = function (ctx, color) {
|
||||
// draw a stack of three disks
|
||||
var w = this.symbolWidth(),
|
||||
|
@ -980,6 +1121,29 @@ SymbolMorph.prototype.renderSymbolBrush = function (ctx, color) {
|
|||
ctx.stroke();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolTick = function (ctx, color) {
|
||||
// draw a check mark
|
||||
var w = this.symbolWidth(),
|
||||
h = this.size;
|
||||
|
||||
ctx.fillStyle = color.toString();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(w * 0.2, h * 0.5);
|
||||
ctx.lineTo(w * 0.5, h);
|
||||
ctx.lineTo(w * 0.8, h * 0.3);
|
||||
ctx.lineTo(w, 0);
|
||||
ctx.lineTo(w * 0.65, h * 0.2);
|
||||
ctx.lineTo(w * 0.5, h * 0.65);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolCheckedBox = function (ctx, color) {
|
||||
// draw a rectangle with a check mark
|
||||
this.renderSymbolRectangle(ctx, color);
|
||||
this.renderSymbolTick(ctx, color);
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolRectangle = function (ctx, color) {
|
||||
// draw a rectangle
|
||||
var w = this.symbolWidth(),
|
||||
|
@ -1348,6 +1512,45 @@ SymbolMorph.prototype.renderSymbolArrowUpOutline = function (ctx, color) {
|
|||
ctx.stroke();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowUpThin = function (ctx, color) {
|
||||
// draw a thin up arrow
|
||||
var w = this.symbolWidth(),
|
||||
h = this.size,
|
||||
n = w / 3,
|
||||
l = Math.max(w / 20, 0.5);
|
||||
|
||||
ctx.strokeStyle = color.toString();
|
||||
ctx.lineWidth = l * 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(w - n, n);
|
||||
ctx.lineTo(w / 2, l * 2);
|
||||
ctx.lineTo(n, n);
|
||||
ctx.moveTo(w / 2, l * 2);
|
||||
ctx.lineTo(w / 2, h - l);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowUpDownThin = function (ctx, color) {
|
||||
// draw a thin up-down arrow
|
||||
var w = this.symbolWidth(),
|
||||
h = this.size,
|
||||
n = w / 3,
|
||||
l = Math.max(w / 20, 0.5);
|
||||
|
||||
ctx.strokeStyle = color.toString();
|
||||
ctx.lineWidth = l * 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(w - n, n);
|
||||
ctx.lineTo(w / 2, l * 2);
|
||||
ctx.lineTo(n, n);
|
||||
ctx.moveTo(w - n, h - n);
|
||||
ctx.lineTo(w / 2, h - l * 2);
|
||||
ctx.lineTo(n, h - n);
|
||||
ctx.moveTo(w / 2, l * 2);
|
||||
ctx.lineTo(w / 2, h - l * 2);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowDown = function (ctx, color) {
|
||||
// draw a down arrow
|
||||
var w = this.symbolWidth();
|
||||
|
@ -1368,6 +1571,16 @@ SymbolMorph.prototype.renderSymbolArrowDownOutline = function (ctx, color) {
|
|||
ctx.restore();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowDownThin = function (ctx, color) {
|
||||
// draw a thin down arrow
|
||||
var w = this.symbolWidth();
|
||||
ctx.save();
|
||||
ctx.translate(w, w);
|
||||
ctx.rotate(radians(180));
|
||||
this.renderSymbolArrowUpThin(ctx, color);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowLeft = function (ctx, color) {
|
||||
// draw a left arrow
|
||||
var w = this.symbolWidth();
|
||||
|
@ -1388,6 +1601,26 @@ SymbolMorph.prototype.renderSymbolArrowLeftOutline = function (ctx, color) {
|
|||
ctx.restore();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowLeftThin = function (ctx, color) {
|
||||
// draw a thin left arrow
|
||||
var w = this.symbolWidth();
|
||||
ctx.save();
|
||||
ctx.translate(0, w);
|
||||
ctx.rotate(radians(-90));
|
||||
this.renderSymbolArrowUpThin(ctx, color);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowLeftRightThin = function (ctx, color) {
|
||||
// draw a thin left-right arrow
|
||||
var w = this.symbolWidth();
|
||||
ctx.save();
|
||||
ctx.translate(0, w);
|
||||
ctx.rotate(radians(-90));
|
||||
this.renderSymbolArrowUpDownThin(ctx, color);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowRight = function (ctx, color) {
|
||||
// draw a right arrow
|
||||
var w = this.symbolWidth();
|
||||
|
@ -1408,6 +1641,16 @@ SymbolMorph.prototype.renderSymbolArrowRightOutline = function (ctx, color) {
|
|||
ctx.restore();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolArrowRightThin = function (ctx, color) {
|
||||
// draw a thin right arrow
|
||||
var w = this.symbolWidth();
|
||||
ctx.save();
|
||||
ctx.translate(w, 0);
|
||||
ctx.rotate(radians(90));
|
||||
this.renderSymbolArrowUpThin(ctx, color);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolRobot = function (ctx, color) {
|
||||
// draw a humanoid robot
|
||||
var w = this.symbolWidth(),
|
||||
|
@ -1758,7 +2001,11 @@ SymbolMorph.prototype.renderSymbolKeyboardFilled = function (ctx, color) {
|
|||
ctx.fillRect(0, 0, w, h);
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolGlobe = function (ctx, color) {
|
||||
SymbolMorph.prototype.renderSymbolGlobeBig = function (ctx, color) {
|
||||
this.renderSymbolGlobe(ctx, color, true);
|
||||
};
|
||||
|
||||
SymbolMorph.prototype.renderSymbolGlobe = function (ctx, color, detailed) {
|
||||
// draw a stylized globe
|
||||
var w = this.symbolWidth(),
|
||||
l = Math.max(w / 30, 0.5);
|
||||
|
@ -1770,15 +2017,14 @@ SymbolMorph.prototype.renderSymbolGlobe = function (ctx, color) {
|
|||
ctx.arc(w / 2, w / 2, w / 2 - l, radians(0), radians(360), false);
|
||||
ctx.stroke();
|
||||
|
||||
// more detailed version, commmented out
|
||||
/*
|
||||
ctx.moveTo(l, w / 3);
|
||||
ctx.lineTo(w - l, w / 3);
|
||||
ctx.stroke();
|
||||
ctx.moveTo(l, 2 * w / 3);
|
||||
ctx.lineTo(w - l, 2 * w / 3);
|
||||
ctx.stroke();
|
||||
*/
|
||||
if (detailed) {
|
||||
ctx.moveTo(l * 3, w * 0.3);
|
||||
ctx.lineTo(w - l * 3, w * 0.3);
|
||||
ctx.stroke();
|
||||
ctx.moveTo(l * 3, w * 0.7);
|
||||
ctx.lineTo(w - l * 3, w * 0.7);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// single line version, looks better when small:
|
||||
ctx.beginPath();
|
||||
|
@ -1788,12 +2034,12 @@ SymbolMorph.prototype.renderSymbolGlobe = function (ctx, color) {
|
|||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(w / 2, l / 2);
|
||||
ctx.arcTo(0, w / 2, w / 2, w, w * 0.66);
|
||||
ctx.arcTo(0, w / 2, w / 2, w, w * 0.75);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(w / 2, l / 2);
|
||||
ctx.arcTo(w, w / 2, w / 2, w, w * 0.66);
|
||||
ctx.arcTo(w, w / 2, w / 2, w, w * 0.75);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
|
|
|
@ -64,13 +64,13 @@
|
|||
|
||||
// Global settings /////////////////////////////////////////////////////
|
||||
|
||||
/*global modules, Point, Morph, fontHeight, SliderMorph, isString,
|
||||
/*global modules, Point, Morph, fontHeight, SliderMorph, isString, detect,
|
||||
MorphicPreferences, FrameMorph, HandleMorph, DialogBoxMorph, StringMorph,
|
||||
SpriteMorph, Context, Costume, BlockEditorMorph, SymbolMorph, List,
|
||||
SpriteMorph, Context, Costume, BlockEditorMorph, SymbolMorph, List, IDE_Morph,
|
||||
SyntaxElementMorph, MenuMorph, SpriteBubbleMorph, SpeechBubbleMorph, Sound,
|
||||
CellMorph, ListWatcherMorph, isNil, BoxMorph, Variable, isSnapObject*/
|
||||
|
||||
modules.tables = '2020-May-07';
|
||||
modules.tables = '2020-May-18';
|
||||
|
||||
var Table;
|
||||
var TableCellMorph;
|
||||
|
@ -1007,6 +1007,7 @@ TableMorph.prototype.resizeCells = function (pos) {
|
|||
this.rows = this.visibleRows();
|
||||
this.buildCells();
|
||||
this.resizeAnchor = pos;
|
||||
this.changed();
|
||||
};
|
||||
|
||||
TableMorph.prototype.columnAt = function (relativeX) {
|
||||
|
@ -1029,6 +1030,23 @@ TableMorph.prototype.userMenu = function () {
|
|||
menu.addItem('reset columns', 'resetColumns');
|
||||
menu.addLine();
|
||||
}
|
||||
if (this.table instanceof List && this.table.canBeJSON()) {
|
||||
menu.addItem(
|
||||
'blockify',
|
||||
() => {
|
||||
var world = this.world(),
|
||||
ide = detect(
|
||||
world.children,
|
||||
m => m instanceof IDE_Morph
|
||||
);
|
||||
this.table.blockify().pickUp(world);
|
||||
world.hand.grabOrigin = {
|
||||
origin: ide.palette,
|
||||
position: ide.palette.center()
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
menu.addItem('open in another dialog...', 'openInDialog');
|
||||
return menu;
|
||||
}
|
||||
|
@ -1037,6 +1055,20 @@ TableMorph.prototype.userMenu = function () {
|
|||
menu.addItem('reset columns', 'resetColumns');
|
||||
}
|
||||
menu.addItem('list view...', 'showListView');
|
||||
if (this.table instanceof List && this.table.canBeJSON()) {
|
||||
menu.addItem(
|
||||
'blockify',
|
||||
() => {
|
||||
var world = this.world(),
|
||||
ide = detect(world.children, m => m instanceof IDE_Morph);
|
||||
this.table.blockify().pickUp(world);
|
||||
world.hand.grabOrigin = {
|
||||
origin: ide.palette,
|
||||
position: ide.palette.center()
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
menu.addLine();
|
||||
menu.addItem('open in dialog...', 'openInDialog');
|
||||
return menu;
|
||||
|
@ -1048,6 +1080,7 @@ TableMorph.prototype.resetColumns = function () {
|
|||
this.columns = this.columnsLayout();
|
||||
this.rows = this.visibleRows();
|
||||
this.buildCells();
|
||||
this.changed();
|
||||
};
|
||||
|
||||
TableMorph.prototype.openInDialog = function () {
|
||||
|
|
324
src/threads.js
324
src/threads.js
|
@ -58,10 +58,10 @@ MultiArgMorph, Point, ReporterBlockMorph, SyntaxElementMorph, contains, Costume,
|
|||
degrees, detect, nop, radians, ReporterSlotMorph, CSlotMorph, RingMorph, Sound,
|
||||
IDE_Morph, ArgLabelMorph, localize, XML_Element, hex_sha512, TableDialogMorph,
|
||||
StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy, Map,
|
||||
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, Color,
|
||||
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, BLACK,
|
||||
TableFrameMorph, ColorSlotMorph, isSnapObject, newCanvas, Symbol, SVG_Costume*/
|
||||
|
||||
modules.threads = '2020-May-13';
|
||||
modules.threads = '2020-July-02';
|
||||
|
||||
var ThreadManager;
|
||||
var Process;
|
||||
|
@ -70,6 +70,19 @@ var Variable;
|
|||
var VariableFrame;
|
||||
var JSCompiler;
|
||||
|
||||
const NONNUMBERS = [true, false, ''];
|
||||
|
||||
(function () {
|
||||
// "zum Schneckengang verdorben, was Adlerflug geworden wäre"
|
||||
// collecting edge-cases that somebody complained about
|
||||
// on Github. Folks, take it easy and keep it fun, okay?
|
||||
// Shit like this is patently ugly and slows Snap down. Tnx!
|
||||
for (var i = 9; i <= 13; i += 1) {
|
||||
NONNUMBERS.push(String.fromCharCode(i));
|
||||
}
|
||||
NONNUMBERS.push(String.fromCharCode(160));
|
||||
})();
|
||||
|
||||
function snapEquals(a, b) {
|
||||
if (a instanceof List || (b instanceof List)) {
|
||||
if (a instanceof List && (b instanceof List)) {
|
||||
|
@ -79,22 +92,11 @@ function snapEquals(a, b) {
|
|||
}
|
||||
|
||||
var x = +a,
|
||||
y = +b,
|
||||
i,
|
||||
specials = [true, false, ''];
|
||||
|
||||
// "zum Schneckengang verdorben, was Adlerflug geworden wäre"
|
||||
// collecting edge-cases that somebody complained about
|
||||
// on Github. Folks, take it easy and keep it fun, okay?
|
||||
// Shit like this is patently ugly and slows Snap down. Tnx!
|
||||
for (i = 9; i <= 13; i += 1) {
|
||||
specials.push(String.fromCharCode(i));
|
||||
}
|
||||
specials.push(String.fromCharCode(160));
|
||||
y = +b;
|
||||
|
||||
// check for special values before coercing to numbers
|
||||
if (isNaN(x) || isNaN(y) ||
|
||||
[a, b].some(any => contains(specials, any) ||
|
||||
[a, b].some(any => contains(NONNUMBERS, any) ||
|
||||
(isString(any) && (any.indexOf(' ') > -1)))
|
||||
) {
|
||||
x = a;
|
||||
|
@ -741,7 +743,15 @@ Process.prototype.evaluateBlock = function (block, argCount) {
|
|||
selector === 'reportAnd' ||
|
||||
selector === 'reportIfElse' ||
|
||||
selector === 'doReport') {
|
||||
return this[selector](block);
|
||||
if (this.isCatchingErrors) {
|
||||
try {
|
||||
return this[selector](block);
|
||||
} catch (error) {
|
||||
this.handleError(error, block);
|
||||
}
|
||||
} else {
|
||||
return this[selector](block);
|
||||
}
|
||||
}
|
||||
|
||||
// first evaluate all inputs, then apply the primitive
|
||||
|
@ -780,13 +790,17 @@ Process.prototype.reportOr = function (block) {
|
|||
|
||||
if (inputs.length < 1) {
|
||||
this.evaluateNextInput(block);
|
||||
} else if (inputs[0]) {
|
||||
if (this.flashContext()) {return; }
|
||||
this.returnValueToParentContext(true);
|
||||
this.popContext();
|
||||
} else if (inputs.length < 2) {
|
||||
this.evaluateNextInput(block);
|
||||
} else if (inputs.length === 1) {
|
||||
// this.assertType(inputs[0], 'Boolean');
|
||||
if (inputs[0]) {
|
||||
if (this.flashContext()) {return; }
|
||||
this.returnValueToParentContext(true);
|
||||
this.popContext();
|
||||
} else {
|
||||
this.evaluateNextInput(block);
|
||||
}
|
||||
} else {
|
||||
// this.assertType(inputs[1], 'Boolean');
|
||||
if (this.flashContext()) {return; }
|
||||
this.returnValueToParentContext(inputs[1] === true);
|
||||
this.popContext();
|
||||
|
@ -798,13 +812,17 @@ Process.prototype.reportAnd = function (block) {
|
|||
|
||||
if (inputs.length < 1) {
|
||||
this.evaluateNextInput(block);
|
||||
} else if (!inputs[0]) {
|
||||
if (this.flashContext()) {return; }
|
||||
this.returnValueToParentContext(false);
|
||||
this.popContext();
|
||||
} else if (inputs.length < 2) {
|
||||
this.evaluateNextInput(block);
|
||||
} else if (inputs.length === 1) {
|
||||
// this.assertType(inputs[0], 'Boolean');
|
||||
if (!inputs[0]) {
|
||||
if (this.flashContext()) {return; }
|
||||
this.returnValueToParentContext(false);
|
||||
this.popContext();
|
||||
} else {
|
||||
this.evaluateNextInput(block);
|
||||
}
|
||||
} else {
|
||||
// this.assertType(inputs[1], 'Boolean');
|
||||
if (this.flashContext()) {return; }
|
||||
this.returnValueToParentContext(inputs[1] === true);
|
||||
this.popContext();
|
||||
|
@ -1096,7 +1114,9 @@ Process.prototype.evaluate = function (
|
|||
args,
|
||||
isCommand
|
||||
) {
|
||||
if (!context) {return null; }
|
||||
if (!context) {
|
||||
return this.returnValueToParentContext(null);
|
||||
}
|
||||
if (context instanceof Function) {
|
||||
// if (!this.enableJS) {
|
||||
// throw new Error('JavaScript is not enabled');
|
||||
|
@ -1342,7 +1362,7 @@ Process.prototype.doStopCustomBlock = function () {
|
|||
Process.prototype.doCallCC = function (aContext, isReporter) {
|
||||
this.evaluate(
|
||||
aContext,
|
||||
new List([this.context.continuation()]),
|
||||
new List([this.context.continuation(isReporter)]),
|
||||
!isReporter
|
||||
);
|
||||
};
|
||||
|
@ -1742,6 +1762,7 @@ Process.prototype.doAddToList = function (element, list) {
|
|||
this.assertType(list, 'list');
|
||||
if (list.type) {
|
||||
this.assertType(element, list.type);
|
||||
list = this.shadowListAttribute(list);
|
||||
}
|
||||
list.add(element);
|
||||
};
|
||||
|
@ -1749,6 +1770,9 @@ Process.prototype.doAddToList = function (element, list) {
|
|||
Process.prototype.doDeleteFromList = function (index, list) {
|
||||
var idx = index;
|
||||
this.assertType(list, 'list');
|
||||
if (list.type) {
|
||||
list = this.shadowListAttribute(list);
|
||||
}
|
||||
if (this.inputOption(index) === 'all') {
|
||||
return list.clear();
|
||||
}
|
||||
|
@ -1768,6 +1792,7 @@ Process.prototype.doInsertInList = function (element, index, list) {
|
|||
this.assertType(list, 'list');
|
||||
if (list.type) {
|
||||
this.assertType(element, list.type);
|
||||
list = this.shadowListAttribute(list);
|
||||
}
|
||||
if (index === '') {
|
||||
return null;
|
||||
|
@ -1786,6 +1811,7 @@ Process.prototype.doReplaceInList = function (index, list, element) {
|
|||
this.assertType(list, 'list');
|
||||
if (list.type) {
|
||||
this.assertType(element, list.type);
|
||||
list = this.shadowListAttribute(list);
|
||||
}
|
||||
if (index === '') {
|
||||
return null;
|
||||
|
@ -1799,9 +1825,27 @@ Process.prototype.doReplaceInList = function (index, list, element) {
|
|||
list.put(element, idx);
|
||||
};
|
||||
|
||||
Process.prototype.shadowListAttribute = function (list) {
|
||||
// private - check whether the list is an attribute that needs to be
|
||||
// shadowed. Use only on typed lists for performance.
|
||||
var rcvr;
|
||||
if (list.type === 'costume' || list.type === 'sound') {
|
||||
rcvr = this.blockReceiver();
|
||||
if (list === rcvr.costumes) {
|
||||
rcvr.shadowAttribute('costumes');
|
||||
list = rcvr.costumes;
|
||||
} else if (list === rcvr.sounds) {
|
||||
rcvr.shadowAttribute('sounds');
|
||||
list = rcvr.sounds;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
// Process accessing list elements - hyper dyadic
|
||||
|
||||
Process.prototype.reportListItem = function (index, list) {
|
||||
var rank;
|
||||
this.assertType(list, 'list');
|
||||
if (index === '') {
|
||||
return '';
|
||||
|
@ -1812,40 +1856,69 @@ Process.prototype.reportListItem = function (index, list) {
|
|||
if (this.inputOption(index) === 'last') {
|
||||
return list.at(list.length());
|
||||
}
|
||||
if (this.enableHyperOps) {
|
||||
if (this.isMatrix(index)) {
|
||||
len = index.length();
|
||||
if (index.length() === 1) {
|
||||
// apply column indices to every row in the table
|
||||
return new List(
|
||||
list.asArray().map(row =>
|
||||
this.reportListItem(
|
||||
index.at(1),
|
||||
row
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
return this.reportListItem(
|
||||
index.cdr(),
|
||||
this.reportListItem(
|
||||
index.at(1),
|
||||
list
|
||||
)
|
||||
);
|
||||
}
|
||||
if (index instanceof List) {
|
||||
rank = this.rank(index);
|
||||
if (rank > 0 && this.enableHyperOps) {
|
||||
if (rank === 1) {
|
||||
if (index.isEmpty()) {
|
||||
return new List(list.asArray().map(each => each));
|
||||
return list.map(item => item);
|
||||
}
|
||||
return new List(
|
||||
index.asArray().map(each => this.reportListItem(each, list))
|
||||
);
|
||||
return index.map(idx => list.at(idx));
|
||||
}
|
||||
return this.reportItems(index, list);
|
||||
}
|
||||
return list.at(index);
|
||||
};
|
||||
|
||||
Process.prototype.reportItems = function (indices, list) {
|
||||
// This. This is it. The pinnacle of my programmer's life.
|
||||
// After days of roaming about my house and garden,
|
||||
// of taking showers and rummaging through the fridge,
|
||||
// of strumming the charango and the five ukuleles
|
||||
// sitting next to my laptop on my desk,
|
||||
// and of letting my mind wander far and wide,
|
||||
// to come up with this design, always thinking
|
||||
// "What would Brian do?".
|
||||
// And look, Ma, it's turned out all beautiful! -jens
|
||||
|
||||
return makeSelector(
|
||||
this.rank(list),
|
||||
indices.cdr(),
|
||||
makeLeafSelector(indices.at(1))
|
||||
)(list);
|
||||
|
||||
function makeSelector(rank, indices, next) {
|
||||
if (rank === 1) {
|
||||
return next;
|
||||
}
|
||||
return makeSelector(
|
||||
rank - 1,
|
||||
indices.cdr(),
|
||||
makeBranch(
|
||||
indices.at(1) || new List(),
|
||||
next
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function makeBranch(indices, next) {
|
||||
return function(data) {
|
||||
if (indices.isEmpty()) {
|
||||
return data.map(item => next(item));
|
||||
}
|
||||
return indices.map(idx => next(data.at(idx)));
|
||||
};
|
||||
}
|
||||
|
||||
function makeLeafSelector(indices) {
|
||||
return function (data) {
|
||||
if (indices.isEmpty()) {
|
||||
return data.map(item => item);
|
||||
}
|
||||
return indices.map(idx => data.at(idx));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Process - other basic list accessors
|
||||
|
||||
Process.prototype.reportListLength = function (list) {
|
||||
|
@ -1892,20 +1965,22 @@ Process.prototype.reportBasicNumbers = function (start, end) {
|
|||
// answer a new arrayed list containing an linearly ascending progression
|
||||
// of integers beginning at start to end.
|
||||
var result, len, i,
|
||||
n = start;
|
||||
s = +start,
|
||||
e = +end,
|
||||
n = s;
|
||||
|
||||
this.assertType(start, 'number');
|
||||
this.assertType(end, 'number');
|
||||
this.assertType(s, 'number');
|
||||
this.assertType(e, 'number');
|
||||
|
||||
if (end > start) {
|
||||
len = Math.floor(end - start);
|
||||
if (e > s) {
|
||||
len = Math.floor(e - s);
|
||||
result = new Array(len);
|
||||
for(i = 0; i <= len; i += 1) {
|
||||
result[i] = n;
|
||||
n += 1;
|
||||
}
|
||||
} else {
|
||||
len = Math.floor(start - end);
|
||||
len = Math.floor(s - e);
|
||||
result = new Array(len);
|
||||
for(i = 0; i <= len; i += 1) {
|
||||
result[i] = n;
|
||||
|
@ -1918,6 +1993,9 @@ Process.prototype.reportBasicNumbers = function (start, end) {
|
|||
Process.prototype.reportConcatenatedLists = function (lists) {
|
||||
var first, result, rows, row, rowIdx, cols, col;
|
||||
this.assertType(lists, 'list');
|
||||
if (lists.isEmpty()) {
|
||||
return lists;
|
||||
}
|
||||
first = lists.at(1);
|
||||
this.assertType(first, 'list');
|
||||
if (first.isLinked) { // link everything
|
||||
|
@ -1925,19 +2003,6 @@ Process.prototype.reportConcatenatedLists = function (lists) {
|
|||
}
|
||||
|
||||
// in case the first sub-list is arrayed
|
||||
|
||||
// fast version, has the disadvantage that it might
|
||||
// change the structure of the source(s) by calling
|
||||
// asArray().
|
||||
// commented out for now
|
||||
/*
|
||||
elements = lists.asArray().map(sub => {
|
||||
this.assertType(sub, 'list');
|
||||
return sub.asArray();
|
||||
});
|
||||
return new List([].concat(...elements));
|
||||
*/
|
||||
|
||||
result = [];
|
||||
rows = lists.length();
|
||||
for (rowIdx = 1; rowIdx <= rows; rowIdx += 1) {
|
||||
|
@ -1954,7 +2019,7 @@ Process.prototype.reportConcatenatedLists = function (lists) {
|
|||
Process.prototype.concatenateLinkedLists = function (lists) {
|
||||
var first;
|
||||
if (lists.isEmpty()) {
|
||||
return new List();
|
||||
return lists;
|
||||
}
|
||||
first = lists.at(1);
|
||||
this.assertType(first, 'list');
|
||||
|
@ -2015,6 +2080,7 @@ Process.prototype.doIf = function () {
|
|||
outer = this.context.outerContext, // for tail call elimination
|
||||
isCustomBlock = this.context.isCustomBlock;
|
||||
|
||||
// this.assertType(args[0], ['Boolean']);
|
||||
this.popContext();
|
||||
if (args[0]) {
|
||||
if (args[1]) {
|
||||
|
@ -2030,6 +2096,7 @@ Process.prototype.doIfElse = function () {
|
|||
outer = this.context.outerContext, // for tail call elimination
|
||||
isCustomBlock = this.context.isCustomBlock;
|
||||
|
||||
// this.assertType(args[0], ['Boolean']);
|
||||
this.popContext();
|
||||
if (args[0]) {
|
||||
if (args[1]) {
|
||||
|
@ -2058,11 +2125,14 @@ Process.prototype.reportIfElse = function (block) {
|
|||
if (this.flashContext()) {return; }
|
||||
this.returnValueToParentContext(inputs.pop());
|
||||
this.popContext();
|
||||
} else if (inputs[0]) {
|
||||
this.evaluateNextInput(block);
|
||||
} else {
|
||||
inputs.push(null);
|
||||
this.evaluateNextInput(block);
|
||||
// this.assertType(inputs[0], ['Boolean']);
|
||||
if (inputs[0]) {
|
||||
this.evaluateNextInput(block);
|
||||
} else {
|
||||
inputs.push(null);
|
||||
this.evaluateNextInput(block);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2316,6 +2386,7 @@ Process.prototype.doRepeat = function (counter, body) {
|
|||
};
|
||||
|
||||
Process.prototype.doUntil = function (goalCondition, body) {
|
||||
// this.assertType(goalCondition, ['Boolean']);
|
||||
if (goalCondition) {
|
||||
this.popContext();
|
||||
this.pushContext('doYield');
|
||||
|
@ -2330,6 +2401,7 @@ Process.prototype.doUntil = function (goalCondition, body) {
|
|||
};
|
||||
|
||||
Process.prototype.doWaitUntil = function (goalCondition) {
|
||||
// this.assertType(goalCondition, ['Boolean']);
|
||||
if (goalCondition) {
|
||||
this.popContext();
|
||||
this.pushContext('doYield');
|
||||
|
@ -2605,7 +2677,7 @@ Process.prototype.reportFindFirst = function (predicate, list) {
|
|||
this.context.accumulator.source.cdr();
|
||||
}
|
||||
if (this.context.accumulator.remaining === 0) {
|
||||
this.returnValueToParentContext(false);
|
||||
this.returnValueToParentContext('');
|
||||
return;
|
||||
}
|
||||
index = this.context.accumulator.idx;
|
||||
|
@ -2626,7 +2698,7 @@ Process.prototype.reportFindFirst = function (predicate, list) {
|
|||
}
|
||||
}
|
||||
if (this.context.accumulator.idx === list.length()) {
|
||||
this.returnValueToParentContext(false);
|
||||
this.returnValueToParentContext('');
|
||||
return;
|
||||
}
|
||||
this.context.accumulator.idx += 1;
|
||||
|
@ -3339,9 +3411,11 @@ Process.prototype.doBroadcast = function (message) {
|
|||
Process.prototype.doBroadcastAndWait = function (message) {
|
||||
if (!this.context.activeSends) {
|
||||
this.context.activeSends = this.doBroadcast(message);
|
||||
this.context.activeSends.forEach(proc =>
|
||||
proc.runStep()
|
||||
);
|
||||
if (this.isRunning()) {
|
||||
this.context.activeSends.forEach(proc =>
|
||||
proc.runStep()
|
||||
);
|
||||
}
|
||||
}
|
||||
this.context.activeSends = this.context.activeSends.filter(proc =>
|
||||
proc.isRunning()
|
||||
|
@ -3486,24 +3560,16 @@ Process.prototype.hyperDyadic = function (baseOp, a, b) {
|
|||
}
|
||||
return new List(result);
|
||||
}
|
||||
return new List(
|
||||
a.asArray().map(each => this.hyperDyadic(baseOp, each, b))
|
||||
);
|
||||
return a.map(each => this.hyperDyadic(baseOp, each, b));
|
||||
}
|
||||
if (this.isMatrix(b)) {
|
||||
return new List(
|
||||
b.asArray().map(each => this.hyperDyadic(baseOp, a, each))
|
||||
);
|
||||
return b.map(each => this.hyperDyadic(baseOp, a, each));
|
||||
}
|
||||
return this.hyperZip(baseOp, a, b);
|
||||
}
|
||||
return baseOp(a, b);
|
||||
};
|
||||
|
||||
Process.prototype.isMatrix = function (value) {
|
||||
return value instanceof List && value.at(1) instanceof List;
|
||||
};
|
||||
|
||||
Process.prototype.hyperZip = function (baseOp, a, b) {
|
||||
// enable dyadic operations to be performed on lists and tables
|
||||
var len, i, result;
|
||||
|
@ -3519,18 +3585,30 @@ Process.prototype.hyperZip = function (baseOp, a, b) {
|
|||
}
|
||||
return new List(result);
|
||||
}
|
||||
return new List(
|
||||
a.asArray().map(each => this.hyperZip(baseOp, each, b))
|
||||
);
|
||||
return a.map(each => this.hyperZip(baseOp, each, b));
|
||||
}
|
||||
if (b instanceof List) {
|
||||
return new List(
|
||||
b.asArray().map(each => this.hyperZip(baseOp, a, each))
|
||||
);
|
||||
return b.map(each => this.hyperZip(baseOp, a, each));
|
||||
}
|
||||
return baseOp(a, b);
|
||||
};
|
||||
|
||||
Process.prototype.isMatrix = function (data) {
|
||||
return data instanceof List && data.at(1) instanceof List;
|
||||
};
|
||||
|
||||
Process.prototype.rank = function(data) {
|
||||
var rank = 0,
|
||||
cur = data;
|
||||
while (cur instanceof List) {
|
||||
rank += 1;
|
||||
cur = cur.at(1);
|
||||
}
|
||||
return rank;
|
||||
};
|
||||
|
||||
// Process math primtives - arithmetic
|
||||
|
||||
Process.prototype.reportSum = function (a, b) {
|
||||
return this.hyperDyadic(this.reportBasicSum, a, b);
|
||||
};
|
||||
|
@ -3613,11 +3691,10 @@ Process.prototype.reportBasicLessThan = function (a, b) {
|
|||
Process.prototype.reportNot = function (bool) {
|
||||
if (this.enableHyperOps) {
|
||||
if (bool instanceof List) {
|
||||
return new List(
|
||||
bool.asArray().map(each => this.reportNot(each))
|
||||
);
|
||||
return bool.map(each => this.reportNot(each));
|
||||
}
|
||||
}
|
||||
// this.assertType(bool, 'Boolean');
|
||||
return !bool;
|
||||
};
|
||||
|
||||
|
@ -3683,9 +3760,7 @@ Process.prototype.reportBoolean = function (bool) {
|
|||
Process.prototype.reportRound = function (n) {
|
||||
if (this.enableHyperOps) {
|
||||
if (n instanceof List) {
|
||||
return new List(
|
||||
n.asArray().map(each => this.reportRound(each))
|
||||
);
|
||||
return n.map(each => this.reportRound(each));
|
||||
}
|
||||
}
|
||||
return Math.round(+n);
|
||||
|
@ -3694,9 +3769,7 @@ Process.prototype.reportRound = function (n) {
|
|||
Process.prototype.reportMonadic = function (fname, n) {
|
||||
if (this.enableHyperOps) {
|
||||
if (n instanceof List) {
|
||||
return new List(
|
||||
n.asArray().map(each => this.reportMonadic(fname, each))
|
||||
);
|
||||
return n.map(each => this.reportMonadic(fname, each));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3756,6 +3829,8 @@ Process.prototype.reportMonadic = function (fname, n) {
|
|||
case '2^':
|
||||
result = Math.pow(2, x);
|
||||
break;
|
||||
case 'id':
|
||||
return n;
|
||||
default:
|
||||
nop();
|
||||
}
|
||||
|
@ -3837,9 +3912,7 @@ Process.prototype.reportBasicLetter = function (idx, string) {
|
|||
Process.prototype.reportStringSize = function (data) {
|
||||
if (this.enableHyperOps) {
|
||||
if (data instanceof List) {
|
||||
return new List(
|
||||
data.asArray().map(each => this.reportStringSize(each))
|
||||
);
|
||||
return data.map(each => this.reportStringSize(each));
|
||||
}
|
||||
}
|
||||
if (data instanceof List) { // catch a common user error
|
||||
|
@ -3853,9 +3926,7 @@ Process.prototype.reportUnicode = function (string) {
|
|||
|
||||
if (this.enableHyperOps) {
|
||||
if (string instanceof List) {
|
||||
return new List(
|
||||
string.asArray().map(each => this.reportUnicode(each))
|
||||
);
|
||||
return string.map(each => this.reportUnicode(each));
|
||||
}
|
||||
str = isNil(string) ? '\u0000' : string.toString();
|
||||
if (str.length > 1) {
|
||||
|
@ -3873,9 +3944,7 @@ Process.prototype.reportUnicode = function (string) {
|
|||
Process.prototype.reportUnicodeAsLetter = function (num) {
|
||||
if (this.enableHyperOps) {
|
||||
if (num instanceof List) {
|
||||
return new List(
|
||||
num.asArray().map(each => this.reportUnicodeAsLetter(each))
|
||||
);
|
||||
return num.map(each => this.reportUnicodeAsLetter(each));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4502,7 +4571,7 @@ Process.prototype.colorAtSprite = function (sprite) {
|
|||
child,
|
||||
i;
|
||||
|
||||
if (!stage) {return new Color(); }
|
||||
if (!stage) {return BLACK; }
|
||||
for (i = stage.children.length; i > 0; i -= 1) {
|
||||
child = stage.children[i - 1];
|
||||
if ((child !== sprite) &&
|
||||
|
@ -4516,7 +4585,7 @@ Process.prototype.colorAtSprite = function (sprite) {
|
|||
if (stage.bounds.containsPoint(point)) {
|
||||
return stage.getPixelColor(point);
|
||||
}
|
||||
return new Color();
|
||||
return BLACK;
|
||||
};
|
||||
|
||||
Process.prototype.colorBelowSprite = function (sprite) {
|
||||
|
@ -4532,7 +4601,7 @@ Process.prototype.colorBelowSprite = function (sprite) {
|
|||
child,
|
||||
i;
|
||||
|
||||
if (!stage) {return new Color(); }
|
||||
if (!stage) {return BLACK; }
|
||||
for (i = 0; i < stage.children.length; i += 1) {
|
||||
if (!found) {
|
||||
child = stage.children[i];
|
||||
|
@ -4549,7 +4618,7 @@ Process.prototype.colorBelowSprite = function (sprite) {
|
|||
if (below.bounds.containsPoint(point)) {
|
||||
return below.getPixelColor(point);
|
||||
}
|
||||
return new Color();
|
||||
return BLACK;
|
||||
};
|
||||
|
||||
Process.prototype.spritesAtPoint = function (point, stage) {
|
||||
|
@ -6070,14 +6139,17 @@ Context.prototype.image = function () {
|
|||
|
||||
// Context continuations:
|
||||
|
||||
Context.prototype.continuation = function () {
|
||||
Context.prototype.continuation = function (isReporter) {
|
||||
var cont;
|
||||
if (this.expression instanceof Array) {
|
||||
cont = this;
|
||||
} else if (this.parentContext) {
|
||||
cont = this.parentContext;
|
||||
} else {
|
||||
cont = new Context(null, 'expectReport');
|
||||
cont = new Context(
|
||||
null,
|
||||
isReporter ? 'expectReport' : 'popContext'
|
||||
);
|
||||
cont.isContinuation = true;
|
||||
return cont;
|
||||
}
|
||||
|
|
|
@ -80,12 +80,12 @@
|
|||
// Global settings /////////////////////////////////////////////////////
|
||||
|
||||
/*global TriggerMorph, modules, Color, Point, BoxMorph, radians, ZERO,
|
||||
StringMorph, Morph, TextMorph, nop, detect, StringFieldMorph,
|
||||
StringMorph, Morph, TextMorph, nop, detect, StringFieldMorph, BLACK, WHITE,
|
||||
HTMLCanvasElement, fontHeight, SymbolMorph, localize, SpeechBubbleMorph,
|
||||
ArrowMorph, MenuMorph, isString, isNil, SliderMorph, MorphicPreferences,
|
||||
ScrollFrameMorph, MenuItemMorph, Note*/
|
||||
|
||||
modules.widgets = '2020-May-06';
|
||||
modules.widgets = '2020-July-01';
|
||||
|
||||
var PushButtonMorph;
|
||||
var ToggleButtonMorph;
|
||||
|
@ -112,8 +112,8 @@ PushButtonMorph.uber = TriggerMorph.prototype;
|
|||
|
||||
PushButtonMorph.prototype.fontSize = 10;
|
||||
PushButtonMorph.prototype.fontStyle = 'sans-serif';
|
||||
PushButtonMorph.prototype.labelColor = new Color(0, 0, 0);
|
||||
PushButtonMorph.prototype.labelShadowColor = new Color(255, 255, 255);
|
||||
PushButtonMorph.prototype.labelColor = BLACK;
|
||||
PushButtonMorph.prototype.labelShadowColor = WHITE;
|
||||
PushButtonMorph.prototype.labelShadowOffset = new Point(1, 1);
|
||||
|
||||
PushButtonMorph.prototype.color = new Color(220, 220, 220);
|
||||
|
@ -820,8 +820,7 @@ ToggleButtonMorph.prototype.previewPath = function (ctx, radius, inset) {
|
|||
};
|
||||
|
||||
ToggleButtonMorph.prototype.createLabel = function () {
|
||||
var shading = !MorphicPreferences.isFlat || this.is3D,
|
||||
none = new Point();
|
||||
var shading = !MorphicPreferences.isFlat || this.is3D;
|
||||
|
||||
if (this.label !== null) {
|
||||
this.label.destroy();
|
||||
|
@ -835,14 +834,14 @@ ToggleButtonMorph.prototype.createLabel = function () {
|
|||
this.trueStateLabel = this.labelString[1].fullCopy();
|
||||
if (!this.isPicture) {
|
||||
this.label.shadowOffset = shading ?
|
||||
this.labelShadowOffset : none;
|
||||
this.labelShadowOffset : ZERO;
|
||||
this.label.shadowColor = this.labelShadowColor;
|
||||
this.label.color = this.labelColor;
|
||||
this.label.fixLayout();
|
||||
this.label.rerender();
|
||||
|
||||
this.trueStateLabel.shadowOffset = shading ?
|
||||
this.labelShadowOffset : none;
|
||||
this.labelShadowOffset : ZERO;
|
||||
this.trueStateLabel.shadowColor = this.labelShadowColor;
|
||||
this.trueStateLabel.color = this.labelColor;
|
||||
this.trueStateLabel.fixLayout();
|
||||
|
@ -880,7 +879,7 @@ ToggleButtonMorph.prototype.createLabel = function () {
|
|||
this.label = this.labelString.fullCopy();
|
||||
if (!this.isPicture) {
|
||||
this.label.shadowOffset = shading ?
|
||||
this.labelShadowOffset : none;
|
||||
this.labelShadowOffset : ZERO;
|
||||
this.label.shadowColor = this.labelShadowColor;
|
||||
this.label.color = this.labelColor;
|
||||
this.label.fixLayout();
|
||||
|
@ -896,7 +895,7 @@ ToggleButtonMorph.prototype.createLabel = function () {
|
|||
true,
|
||||
false,
|
||||
false,
|
||||
shading ? this.labelShadowOffset : none,
|
||||
shading ? this.labelShadowOffset : ZERO,
|
||||
this.labelShadowColor,
|
||||
this.labelColor
|
||||
);
|
||||
|
@ -1464,7 +1463,7 @@ DialogBoxMorph.prototype.titleFontSize = 14;
|
|||
DialogBoxMorph.prototype.fontStyle = 'sans-serif';
|
||||
|
||||
DialogBoxMorph.prototype.color = PushButtonMorph.prototype.color;
|
||||
DialogBoxMorph.prototype.titleTextColor = new Color(255, 255, 255);
|
||||
DialogBoxMorph.prototype.titleTextColor = WHITE;
|
||||
DialogBoxMorph.prototype.titleBarColor
|
||||
= PushButtonMorph.prototype.pressColor;
|
||||
|
||||
|
@ -1536,7 +1535,7 @@ DialogBoxMorph.prototype.inform = function (
|
|||
null,
|
||||
null,
|
||||
MorphicPreferences.isFlat ? null : new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
);
|
||||
|
||||
if (!this.key) {
|
||||
|
@ -1571,7 +1570,7 @@ DialogBoxMorph.prototype.askYesNo = function (
|
|||
null,
|
||||
null,
|
||||
MorphicPreferences.isFlat ? null : new Point(1, 1),
|
||||
new Color(255, 255, 255)
|
||||
WHITE
|
||||
);
|
||||
|
||||
if (!this.key) {
|
||||
|
@ -1715,7 +1714,7 @@ DialogBoxMorph.prototype.promptCode = function (
|
|||
null, // width
|
||||
null, // font name
|
||||
MorphicPreferences.isFlat ? null : new Point(1, 1),
|
||||
new Color(255, 255, 255) // shadowColor
|
||||
WHITE // shadowColor
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1796,7 +1795,7 @@ DialogBoxMorph.prototype.promptVector = function (
|
|||
null, // width
|
||||
null, // font name
|
||||
MorphicPreferences.isFlat ? null : new Point(1, 1),
|
||||
new Color(255, 255, 255) // shadowColor
|
||||
WHITE // shadowColor
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1909,7 +1908,7 @@ DialogBoxMorph.prototype.promptCredentials = function (
|
|||
null, // width
|
||||
null, // font name
|
||||
MorphicPreferences.isFlat ? null : new Point(1, 1),
|
||||
new Color(255, 255, 255) // shadowColor
|
||||
WHITE // shadowColor
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2897,7 +2896,7 @@ InputFieldMorph.prototype.init = function (
|
|||
this.isNumeric = isNumeric || false;
|
||||
|
||||
InputFieldMorph.uber.init.call(this);
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.add(contents);
|
||||
this.add(arrow);
|
||||
contents.isDraggable = false;
|
||||
|
@ -3092,7 +3091,7 @@ InputFieldMorph.prototype.render = function (ctx) {
|
|||
var borderColor;
|
||||
|
||||
if (this.parent) {
|
||||
if (this.parent.color.eq(new Color(255, 255, 255))) {
|
||||
if (this.parent.color.eq(WHITE)) {
|
||||
this.color = this.parent.color.darker(this.contrast * 0.1);
|
||||
} else {
|
||||
this.color = this.parent.color.lighter(this.contrast * 0.75);
|
||||
|
@ -3276,9 +3275,9 @@ PianoMenuMorph.prototype.createItems = function () {
|
|||
this.edge = MorphicPreferences.isFlat ? 0 : 5;
|
||||
this.border = MorphicPreferences.isFlat ? 1 : 2;
|
||||
}
|
||||
this.color = new Color(255, 255, 255);
|
||||
this.color = WHITE;
|
||||
this.borderColor = new Color(60, 60, 60);
|
||||
this.bounds.setExtent(new Point(0, 0));
|
||||
this.bounds.setExtent(ZERO);
|
||||
|
||||
x = this.left() + 1;
|
||||
y = this.top() + (this.fontSize * 1.5) + 2;
|
||||
|
@ -3287,12 +3286,12 @@ PianoMenuMorph.prototype.createItems = function () {
|
|||
blackkey = tuple[0][1] !== " ";
|
||||
key = new BoxMorph(1, 1);
|
||||
if (blackkey) {
|
||||
keycolor = new Color(0, 0, 0);
|
||||
keycolor = BLACK;
|
||||
keywidth = this.fontSize; // 9;
|
||||
keyheight = this.fontSize * 2.5;
|
||||
keyposition = new Point(x + 2 - (this.fontSize * 2), y);
|
||||
} else {
|
||||
keycolor = new Color(255, 255, 255);
|
||||
keycolor = WHITE;
|
||||
keywidth = this.fontSize * 1.5;
|
||||
keyheight = this.fontSize * 4;
|
||||
keyposition = new Point(x + 1, y);
|
||||
|
|
Ładowanie…
Reference in New Issue