Merge branch 'master' into patch-1

pull/95/head
Jens Mönig 2020-07-03 13:05:31 +02:00 zatwierdzone przez GitHub
commit e6d260bf45
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
44 zmienionych plików z 1724 dodań i 809 usunięć

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -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.

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -1 +1 @@
<blocks app="Snap! 5.0, http://snap.berkeley.edu" version="1"><block-definition s="show picture %&apos;bitmap&apos;" 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 _&#xD;</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&#xD;</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,&#xD; result = false;&#xD;&#xD;camDialog = new CamSnapshotDialogMorph(&#xD; this.parentThatIsA(IDE_Morph),&#xD; this,&#xD; function () {result = null; },&#xD; function (costume) {&#xD; result = costume;&#xD; this.close();&#xD; }&#xD;);&#xD;&#xD;camDialog.key = &apos;camera&apos;;&#xD;camDialog.popUp(this.world());&#xD;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&#xD;</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,&#xD; result = false;&#xD;&#xD;camDialog = new CamSnapshotDialogMorph(&#xD; this.parentThatIsA(IDE_Morph),&#xD; this,&#xD; function () {result = null; },&#xD; function (costume) {&#xD; result = costume;&#xD; this.close();&#xD; }&#xD;);&#xD;&#xD;camDialog.key = &apos;camera&apos;;&#xD;camDialog.popUp(this.world());&#xD;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

Wyświetl plik

@ -1 +1 @@
<blocks app="Snap! 5.0, http://snap.berkeley.edu" version="1"><block-definition s="costume from text %&apos;text&apos; size %&apos;size&apos;" type="reporter" category="looks"><header></header><code></code><translations>de:Kostüm aus Text _ Größe _&#xD;pt:um traje com o texto _ de tamanho _&#xD;</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 %&apos;text&apos; size %&apos;size&apos;" type="reporter" category="looks"><header></header><code></code><translations>de:Kostüm aus Text _ Größe _&#xD;pt:um traje com o texto _ de tamanho _&#xD;</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

Wyświetl plik

@ -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':

Wyświetl plik

@ -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'
};

Wyświetl plik

@ -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

Wyświetl plik

@ -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.':

Wyświetl plik

@ -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

Wyświetl plik

@ -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':

Wyświetl plik

@ -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.':

Wyświetl plik

@ -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...':

Wyświetl plik

@ -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...':

Wyświetl plik

@ -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':

Wyświetl plik

@ -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'));

Wyświetl plik

@ -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;
};

Wyświetl plik

@ -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;
};

Wyświetl plik

@ -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';
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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...',

Wyświetl plik

@ -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 = {

Wyświetl plik

@ -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);

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);
};

Wyświetl plik

@ -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);

Wyświetl plik

@ -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();
};

Wyświetl plik

@ -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 () {

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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);