diff --git a/HISTORY.md b/HISTORY.md index cd59cf24..dc378611 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,31 @@ ## in development: +### 2020-07-22 +* morphic, blocks, gui: tweaked block-fading mouse-over +* blocks, threads: tweaked context visualizations to be alpha-independent +* gui: save block-transparency in settings +* morphic: fixed input slider target update rendering + +### 2020-07-21 +* blocks: tweaked block highlights for fade-out +* widgets, gui: tweaked scripts tab for fade-out +* blocks, gui: tweaked default mode colors to slightly darker + +### 2020-07-20 +* objects: fixed a list-watcher direct-editing offset bug +* morphic: update the Hand's position on mouse-down - avoid triggering at the origin point +* symbols: added hooks for dynamic coloring +* blocks: added blocks-fading support for symbols (under construction) +* morphic: tweaked transparency of grabbed morphs + +### 2020-07-19 +* blocks: blocks-fade-out support for label arrows (under construction) +* blocks: blocks-fade-out support for multi-line inputs (under construction) + +### 2020-07-17 +* morphic, blocks: blocks-fadeout (under construction) + ### 2020-07-15 * morphic: made keyboard handler (more) invisible, thanks, Bernat! * gui: made remaining synchronous http requests asynch (url: #open, #run) diff --git a/snap.html b/snap.html index 06626de2..c9c5c913 100755 --- a/snap.html +++ b/snap.html @@ -4,13 +4,13 @@ Snap! Build Your Own Blocks 6.0.1 - dev - - - - - - - - + + + + + + + diff --git a/src/blocks.js b/src/blocks.js index e9b23692..ff21b135 100644 --- a/src/blocks.js +++ b/src/blocks.js @@ -65,6 +65,12 @@ BoxMorph* CommentMorph ScriptFocusMorph + StringMorph* + BlockLabelMorph + InputSlotStringMorph + InputSlotTextMorph + SymbolMorph* + BlockSymbolMorph * from morphic.js @@ -75,6 +81,8 @@ defined. Use this list to locate code in this document: SyntaxElementMorph + BlockLabelMorph + BlockSymbolMorph BlockMorph CommandBlockMorph HatBlockMorph @@ -86,6 +94,8 @@ RingCommandSlotMorph CSlotMorph InputSlotMorph + InputSlotStringMorph + InputSlotTextMorph BooleanSlotMorph ArrowMorph TextSlotMorph @@ -142,16 +152,18 @@ document, getDocumentPositionOf, isNaN, isString, newCanvas, nop, parseFloat, radians, useBlurredShadows, SpeechBubbleMorph, modules, StageMorph, Sound, fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph, Rectangle, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, WHITE, BLACK, -Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil, +Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil, CLEAR, isSnapObject, PushButtonMorph, SpriteIconMorph, Process, AlignmentMorph, CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2020-July-15'; +modules.blocks = '2020-July-22'; var SyntaxElementMorph; var BlockMorph; +var BlockLabelMorph; +var BlockSymbolMorph; var CommandBlockMorph; var ReporterBlockMorph; var ScriptsMorph; @@ -159,6 +171,8 @@ var ArgMorph; var CommandSlotMorph; var CSlotMorph; var InputSlotMorph; +var InputSlotStringMorph; +var InputSlotTextMorph; var BooleanSlotMorph; var ArrowMorph; var ColorSlotMorph; @@ -771,6 +785,15 @@ SyntaxElementMorph.prototype.unflash = function () { } }; +SyntaxElementMorph.prototype.doWithAlpha = function (alpha, callback) { + var current = SyntaxElementMorph.prototype.alpha, + result; + SyntaxElementMorph.prototype.alpha = alpha; + result = callback(); + SyntaxElementMorph.prototype.alpha = current; + return result; +}; + // SyntaxElementMorph zebra coloring SyntaxElementMorph.prototype.fixBlockColor = function ( @@ -1567,12 +1590,13 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.add(this.labelPart('%loopArrow')); break; case '%loopArrow': - part = new SymbolMorph('loop'); + part = new BlockSymbolMorph('loop'); part.size = this.fontSize * 0.7; part.color = WHITE; part.shadowColor = this.color.darker(this.labelContrast); part.shadowOffset = MorphicPreferences.isFlat ? ZERO : this.embossing; + part.isFading = true; part.fixLayout(); break; case '%clr': @@ -1625,7 +1649,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { // symbols: case '%turtle': - part = new SymbolMorph('turtle'); + part = new BlockSymbolMorph('turtle'); part.size = this.fontSize * 1.2; part.color = WHITE; part.shadowColor = this.color.darker(this.labelContrast); @@ -1634,7 +1658,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%turtleOutline': - part = new SymbolMorph('turtleOutline'); + part = new BlockSymbolMorph('turtleOutline'); part.size = this.fontSize; part.color = WHITE; part.isProtectedLabel = true; // doesn't participate in zebraing @@ -1644,7 +1668,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%clockwise': - part = new SymbolMorph('turnRight'); + part = new BlockSymbolMorph('turnRight'); part.size = this.fontSize * 1.5; part.color = WHITE; part.isProtectedLabel = false; // zebra colors @@ -1654,7 +1678,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%counterclockwise': - part = new SymbolMorph('turnLeft'); + part = new BlockSymbolMorph('turnLeft'); part.size = this.fontSize * 1.5; part.color = WHITE; part.isProtectedLabel = false; // zebra colors @@ -1664,7 +1688,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%greenflag': - part = new SymbolMorph('flag'); + part = new BlockSymbolMorph('flag'); part.size = this.fontSize * 1.5; part.color = new Color(0, 200, 0); part.isProtectedLabel = true; // doesn't participate in zebraing @@ -1674,7 +1698,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%stop': - part = new SymbolMorph('octagon'); + part = new BlockSymbolMorph('octagon'); part.size = this.fontSize * 1.5; part.color = new Color(200, 0, 0); part.isProtectedLabel = true; // doesn't participate in zebraing @@ -1684,7 +1708,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%pause': - part = new SymbolMorph('pause'); + part = new BlockSymbolMorph('pause'); part.size = this.fontSize; part.color = new Color(255, 220, 0); part.isProtectedLabel = true; // doesn't participate in zebraing @@ -1694,7 +1718,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%blitz': - part = new SymbolMorph('flash'); + part = new BlockSymbolMorph('flash'); part.size = this.fontSize; part.color = WHITE; part.isProtectedLabel = false; // zebra colors @@ -1704,7 +1728,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fixLayout(); break; case '%list': - part = new SymbolMorph('list'); + part = new BlockSymbolMorph('list'); part.size = this.fontSize; part.color = WHITE; part.shadowColor = this.color.darker(this.labelContrast); @@ -1756,7 +1780,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part.fontStyle = this.labelFontStyle; part.fontSize = this.fontSize * (+tokens[1] || 1); } else { - part = new SymbolMorph(tokens[0]); + part = new BlockSymbolMorph(tokens[0]); part.size = this.fontSize * (+tokens[1] || 1.2); } part.color = new Color( @@ -1770,7 +1794,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { ZERO : this.embossing; part.fixLayout(); } else { - part = new StringMorph( + part = new BlockLabelMorph( spec, // text this.fontSize, // fontSize this.labelFontStyle, // fontStyle @@ -1783,6 +1807,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { WHITE, // color this.labelFontName // fontName ); + } return part; }; @@ -2268,6 +2293,115 @@ SyntaxElementMorph.prototype.mappedCode = function (definitions) { return result; }; +// BlockLabelMorph /////////////////////////////////////////////// + +/* + I am a piece of single-line text written on a block. I serve as a + container for sharing typographic attributes among my instances +*/ + +// BlockLabelMorph inherits from StringMorph: + +BlockLabelMorph.prototype = new StringMorph(); +BlockLabelMorph.prototype.constructor = BlockLabelMorph; +BlockLabelMorph.uber = StringMorph.prototype; + +function BlockLabelMorph( + text, + fontSize, + fontStyle, + bold, + italic, + isNumeric, + shadowOffset, + shadowColor, + color, + fontName +) { + this.init( + text, + fontSize, + fontStyle, + bold, + italic, + isNumeric, + shadowOffset, + shadowColor, + color, + fontName + ); +} +BlockLabelMorph.prototype.getRenderColor = function () { + var block = this.parentThatIsA(BlockMorph); + if (MorphicPreferences.isFlat) { + return block.alpha > 0.4 ? this.color + : (block.alpha > 0.2 ? BLACK + : block.color.solid()); + } + return block.alpha > 0.4 ? this.color + : (block.alpha > 0.2 ? WHITE + : block.color.solid()); +}; + +BlockLabelMorph.prototype.getShadowRenderColor = function () { + return this.parentThatIsA(BlockMorph).alpha > 0.6 ? + this.shadowColor + : CLEAR; +}; + +// BlockSymbolMorph ////////////////////////////////////////////////////////// + +/* + I am a pictogram written on a block. I serve as a + container for sharing typographic attributes among my instances. + NOTE: I have an additional attribute ".isFading" that governs + my behavior when fading out the blocks I'm embedded in +*/ + +// BlockSymbolMorph inherits from SymbolMorph: + +BlockSymbolMorph.prototype = new SymbolMorph(); +BlockSymbolMorph.prototype.constructor = BlockSymbolMorph; +BlockSymbolMorph.uber = SymbolMorph.prototype; + +function BlockSymbolMorph(name, size, color, shadowOffset, shadowColor) { + this.init(name, size, color, shadowOffset, shadowColor); +} + +BlockSymbolMorph.prototype.getRenderColor = function () { + if (MorphicPreferences.isFlat) { + if (this.isFading) { + return this.color.mixed(this.parent.alpha, WHITE); + } + if (this.color.eq(WHITE)) { + return this.parent.alpha > 0.4 ? this.color + : (this.parent.alpha > 0.2 ? BLACK + : this.parent.color.solid()); + } + return this.color; + } + if (this.isFading) { + return this.color.mixed( + this.parent.alpha, + SpriteMorph.prototype.paletteColor + ); + } + if (this.color.eq(BLACK)) { + return this.parent.alpha > 0.4 ? this.color + : (this.parent.alpha > 0.2 ? WHITE + : this.parent.color.solid()); + } + if (this.color.eq(WHITE)) { + return this.parent.alpha > 0.2 ? WHITE + : this.parent.color.solid(); + } + return this.color; +}; + +BlockSymbolMorph.prototype.getShadowRenderColor = function () { + return this.parent.alpha > 0.6 ? this.shadowColor : CLEAR; +}; + // BlockMorph ////////////////////////////////////////////////////////// /* @@ -4078,7 +4212,7 @@ BlockMorph.prototype.highlight = function (color, blur, border) { BlockMorph.prototype.highlightImage = function (color, border) { var fb, img, hi, ctx, out; fb = this.fullBounds().extent(); - img = this.fullImage(); + this.doWithAlpha(1, () => img = this.fullImage()); hi = newCanvas(fb.add(border * 2)); ctx = hi.getContext('2d'); @@ -4108,7 +4242,7 @@ BlockMorph.prototype.highlightImage = function (color, border) { BlockMorph.prototype.highlightImageBlurred = function (color, blur) { var fb, img, hi, ctx; fb = this.fullBounds().extent(); - img = this.fullImage(); + this.doWithAlpha(1, () => img = this.fullImage()); hi = newCanvas(fb.add(blur * 2)); ctx = hi.getContext('2d'); @@ -4368,6 +4502,20 @@ BlockMorph.prototype.activeProcess = function () { return null; }; +BlockMorph.prototype.mouseEnterBounds = function (dragged) { + if (!dragged && this.alpha < 1) { + this.alpha = Math.min(this.alpha + 0.2, 1); + this.rerender(); + } +}; + +BlockMorph.prototype.mouseLeaveBounds = function (dragged) { + if (SyntaxElementMorph.prototype.alpha < 1) { + delete this.alpha; + this.rerender(); + } +}; + // BlockMorph dragging and dropping BlockMorph.prototype.rootForGrab = function () { @@ -5597,7 +5745,7 @@ ReporterBlockMorph.prototype.prepareToBeGrabbed = function (handMorph) { this.setPosition(oldPos); } ReporterBlockMorph.uber.prepareToBeGrabbed.call(this, handMorph); - this.alpha = Math.min(this.alpha, 0.85); + handMorph.alpha = this.alpha < 1 ? 1 : 0.85; this.cachedSlotSpec = null; }; @@ -6439,6 +6587,36 @@ ScriptsMorph.prototype.fullCopy = function () { return cpy; }; +// ScriptsMorph rendering: + +ScriptsMorph.prototype.render = function (aContext) { + aContext.fillStyle = this.getRenderColor().toString(); + aContext.fillRect(0, 0, this.width(), this.height()); + if (this.cachedTexture) { + this.renderCachedTexture(aContext); + } else if (this.texture) { + this.renderTexture(this.texture, aContext); + } +}; + +ScriptsMorph.prototype.getRenderColor = function () { + if (MorphicPreferences.isFlat || + SyntaxElementMorph.prototype.alpha > 0.85) { + return this.color; + } + return this.color.mixed( + Math.max(SyntaxElementMorph.prototype.alpha - 0.15, 0), + SpriteMorph.prototype.paletteColor + ); +}; + +ScriptsMorph.prototype.renderCachedTexture = function (ctx) { + // support blocks-to-text slider + if (SyntaxElementMorph.prototype.alpha > 0.8) { + ScriptsMorph.uber.renderCachedTexture.call(this, ctx); + } +}; + // ScriptsMorph stepping: ScriptsMorph.prototype.step = function () { @@ -7383,6 +7561,9 @@ ArgMorph.prototype.init = function (type) { ArgMorph.uber.init.call(this); this.color = new Color(0, 17, 173); this.createIcon(); + if (type === 'list') { + this.alpha = 1; + } }; // ArgMorph preferences settings: @@ -8556,11 +8737,13 @@ InputSlotMorph.prototype.init = function ( choiceDict, isReadOnly ) { - var contents = new StringMorph(''), + var contents = new InputSlotStringMorph(''), arrow = new ArrowMorph( 'down', 0, - Math.max(Math.floor(this.fontSize / 6), 1) + Math.max(Math.floor(this.fontSize / 6), 1), + BLACK, + true ); contents.fontSize = this.fontSize; @@ -9754,6 +9937,102 @@ InputSlotMorph.prototype.drawRoundBorder = function (ctx) { ctx.stroke(); }; +// InputSlotStringMorph /////////////////////////////////////////////// + +/* + I am a piece of single-line text inside an input slot block. I serve as a + container for sharing typographic attributes among my instances +*/ + +// InputSlotStringMorph inherits from StringMorph: + +InputSlotStringMorph.prototype = new StringMorph(); +InputSlotStringMorph.prototype.constructor = InputSlotStringMorph; +InputSlotStringMorph.uber = StringMorph.prototype; + +function InputSlotStringMorph( + text, + fontSize, + fontStyle, + bold, + italic, + isNumeric, + shadowOffset, + shadowColor, + color, + fontName +) { + this.init( + text, + fontSize, + fontStyle, + bold, + italic, + isNumeric, + shadowOffset, + shadowColor, + color, + fontName + ); +} + +InputSlotStringMorph.prototype.getRenderColor = function () { + if (MorphicPreferences.isFlat) { + if (this.isEditable) { + return this.color; + } + return this.parent.alpha > 0.4 ? this.color : BLACK; + } + return this.parent.alpha > 0.3 ? this.color : WHITE; +}; + +InputSlotStringMorph.prototype.getShadowRenderColor = function () { + return this.parent.alpha > 0.6 ? this.shadowColor : CLEAR; +}; + +// InputSlotTextMorph /////////////////////////////////////////////// + +/* + I am a piece of multi-line text inside an input slot block. I serve as a + container for sharing typographic attributes among my instances +*/ + +// InputSlotTextMorph inherits from TextMorph: + +InputSlotTextMorph.prototype = new TextMorph(); +InputSlotTextMorph.prototype.constructor = InputSlotTextMorph; +InputSlotTextMorph.uber = StringMorph.prototype; + +function InputSlotTextMorph( + text, + fontSize, + fontStyle, + bold, + italic, + alignment, + width, + fontName, + shadowOffset, + shadowColor +) { + this.init(text, + fontSize, + fontStyle, + bold, + italic, + alignment, + width, + fontName, + shadowOffset, + shadowColor); +} + +InputSlotTextMorph.prototype.getRenderColor = + InputSlotStringMorph.prototype.getRenderColor; + +InputSlotTextMorph.prototype.getShadowRenderColor = + InputSlotStringMorph.prototype.getShadowRenderColor; + // TemplateSlotMorph /////////////////////////////////////////////////// /* @@ -9927,6 +10206,7 @@ BooleanSlotMorph.prototype.init = function (initialValue) { this.isUnevaluated = false; this.progress = 0; // for animation state, not persisted BooleanSlotMorph.uber.init.call(this); + this.alpha = 1; this.fixLayout(); }; @@ -10495,14 +10775,15 @@ ArrowMorph.uber = Morph.prototype; // ArrowMorph instance creation: -function ArrowMorph(direction, size, padding, color) { - this.init(direction, size, padding, color); +function ArrowMorph(direction, size, padding, color, isBlockLabel) { + this.init(direction, size, padding, color, isBlockLabel); } -ArrowMorph.prototype.init = function (direction, size, padding, color) { +ArrowMorph.prototype.init = function (direction, size, padding, color, isLbl) { this.direction = direction || 'down'; this.size = size || ((size === 0) ? 0 : 50); this.padding = padding || 0; + this.isBlockLabel = isLbl || false; ArrowMorph.uber.init.call(this); this.color = color || BLACK; @@ -10530,7 +10811,7 @@ ArrowMorph.prototype.render = function (ctx) { w = this.width(), w2 = Math.floor(w / 2); - ctx.fillStyle = this.color.toString(); + ctx.fillStyle = this.getRenderColor().toString(); ctx.beginPath(); if (this.direction === 'down') { ctx.moveTo(pad, h2); @@ -10553,6 +10834,16 @@ ArrowMorph.prototype.render = function (ctx) { ctx.fill(); }; +ArrowMorph.prototype.getRenderColor = function () { + if (this.isBlockLabel) { + if (MorphicPreferences.isFlat) { + return this.color; + } + return SyntaxElementMorph.prototype.alpha > 0.5 ? this.color : WHITE; + } + return this.color; +}; + // TextSlotMorph ////////////////////////////////////////////////////// /* @@ -10578,11 +10869,13 @@ TextSlotMorph.prototype.init = function ( choiceDict, isReadOnly ) { - var contents = new TextMorph(''), + var contents = new InputSlotTextMorph(''), arrow = new ArrowMorph( 'down', 0, - Math.max(Math.floor(this.fontSize / 6), 1) + Math.max(Math.floor(this.fontSize / 6), 1), + BLACK, + true ); contents.fontSize = this.fontSize; @@ -10657,6 +10950,7 @@ function ColorSlotMorph(clr) { ColorSlotMorph.prototype.init = function (clr) { ColorSlotMorph.uber.init.call(this); + this.alpha = 1; this.setColor(clr || new Color(145, 26, 68)); }; @@ -10910,7 +11204,8 @@ MultiArgMorph.prototype.init = function ( 'left', this.fontSize, Math.max(Math.floor(this.fontSize / 6), 1), - arrowColor + arrowColor, + true ); // right arrow: @@ -10918,7 +11213,8 @@ MultiArgMorph.prototype.init = function ( 'right', this.fontSize, Math.max(Math.floor(this.fontSize / 6), 1), - arrowColor + arrowColor, + true ); // control panel: diff --git a/src/gui.js b/src/gui.js index d530b80c..4e30d2ef 100644 --- a/src/gui.js +++ b/src/gui.js @@ -78,7 +78,7 @@ Animation, BoxMorph, BlockEditorMorph, BlockDialogMorph, Note, ZERO, BLACK*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2020-July-16'; +modules.gui = '2020-July-22'; // Declarations @@ -110,7 +110,7 @@ IDE_Morph.uber = Morph.prototype; IDE_Morph.prototype.setDefaultDesign = function () { MorphicPreferences.isFlat = false; - SpriteMorph.prototype.paletteColor = new Color(35, 35, 35); + SpriteMorph.prototype.paletteColor = new Color(30, 30, 30); SpriteMorph.prototype.paletteTextColor = new Color(230, 230, 230); StageMorph.prototype.paletteTextColor = SpriteMorph.prototype.paletteTextColor; @@ -195,10 +195,10 @@ 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(4).toString(); + ctx.fillStyle = this.groupColor.lighter(2).toString(); ctx.fillRect(i + 1, 0, 1, 100); ctx.fillRect(i + 3, 0, 1, 100); - ctx.fillStyle = this.groupColor.toString(); + ctx.fillStyle = this.groupColor.darker(2).toString(); ctx.fillRect(i + 2, 0, 1, 100); } return pic; @@ -1173,6 +1173,7 @@ IDE_Morph.prototype.createCategories = function () { this.categories = new Morph(); this.categories.color = this.groupColor; this.categories.bounds.setWidth(this.paletteWidth); + this.categories.getRenderColor = ScriptsMorph.prototype.getRenderColor; function addCategoryButton(category) { var labelWidth = 75, @@ -1533,6 +1534,18 @@ IDE_Morph.prototype.createSpriteBar = function () { tab.labelShadowOffset = new Point(-1, -1); tab.labelShadowColor = tabColors[1]; tab.labelColor = this.buttonLabelColor; + + tab.getPressRenderColor = function () { + if (MorphicPreferences.isFlat || + SyntaxElementMorph.prototype.alpha > 0.85) { + return this.pressColor; + } + return this.pressColor.mixed( + Math.max(SyntaxElementMorph.prototype.alpha - 0.15, 0), + SpriteMorph.prototype.paletteColor + ); + }; + tab.fixLayout(); tabBar.add(tab); @@ -1783,6 +1796,8 @@ IDE_Morph.prototype.createCorral = function () { this.corral = new Morph(); this.corral.color = this.groupColor; + this.corral.getRenderColor = ScriptsMorph.prototype.getRenderColor; + this.add(this.corral); this.corral.stageIcon = new SpriteIconMorph(this.stage); @@ -2380,6 +2395,7 @@ IDE_Morph.prototype.refreshIDE = function () { IDE_Morph.prototype.applySavedSettings = function () { var design = this.getSetting('design'), zoom = this.getSetting('zoom'), + fade = this.getSetting('fade'), language = this.getSetting('language'), click = this.getSetting('click'), longform = this.getSetting('longform'), @@ -2404,6 +2420,11 @@ IDE_Morph.prototype.applySavedSettings = function () { SpriteMorph.prototype.initBlocks(); } + // blocks fade + if (!isNil(fade)) { + this.setBlockTransparency(+fade); + } + // language if (language && language !== 'en') { this.userLanguage = language; @@ -3112,6 +3133,12 @@ IDE_Morph.prototype.settingsMenu = function () { 'Zoom blocks...', 'userSetBlocksScale' ); +/* + menu.addItem( + 'Fade blocks...', + 'userFadeBlocks' + ); +*/ menu.addItem( 'Stage size...', 'userSetStageSize' @@ -3124,32 +3151,6 @@ IDE_Morph.prototype.settingsMenu = function () { 'before it picks up an object', new Color(100, 0, 0) ); - /* - menu.addItem( - "Block alpha...", - () => { - world.prompt( - 'Block alpha', - alpha => { - SyntaxElementMorph.prototype.setAlphaScaled(alpha); - this.rerender(); - }, - this, - (SyntaxElementMorph.prototype.alpha * 100).toString(), - null, - 0, - 100, - true, - alpha => { - SyntaxElementMorph.prototype.setAlphaScaled(alpha); - this.rerender(); - }, - ); - }, - 'set the blocks\'\nalpha value', - new Color(100, 0, 0) - ); - */ } menu.addItem( 'Microphone resolution...', @@ -5607,7 +5608,9 @@ IDE_Morph.prototype.userSetBlocksScale = function () { sample = new FrameMorph(); sample.acceptsDrops = false; sample.color = IDE_Morph.prototype.groupColor; - sample.cachedTexture = this.scriptsPaneTexture; + if (SyntaxElementMorph.prototype.alpha > 0.8) { + sample.cachedTexture = this.scriptsPaneTexture; + } sample.setExtent(new Point(250, 180)); scrpt.setPosition(sample.position().add(10)); sample.add(scrpt); @@ -5677,6 +5680,63 @@ IDE_Morph.prototype.setBlocksScale = function (num) { this.saveSetting('zoom', num); }; +// IDE_Morph blocks fading + +IDE_Morph.prototype.userFadeBlocks = function () { + var dlg, + initial = 100 - (SyntaxElementMorph.prototype.alpha * 100); + + dlg = new DialogBoxMorph( + null, + num => this.setBlockTransparency(num, true) // and save setting + ).withKey('fadeBlocks'); + if (MorphicPreferences.isTouchDevice) { + dlg.isDraggable = false; + } + + dlg.cancel = () => { + this.setBlockTransparency(initial); + dlg.destroy(); + }; + + dlg.prompt( + 'Fade blocks', + initial.toString(), + this.world(), + null, // pic + { + 'block-solid (0)' : 0, + 'reduced (39)' : 39, + 'medium (49)' : 49, + 'light (59)' : 59, + 'semi (69)' : 69, + 'glassy (79)' : 79, + 'shimmering (80' : 80, + 'elegant (90)' : 90, + 'subtle (95)' : 95, + 'text-only (100)' : 100 + }, + false, // read only? + true, // numeric + 0, // slider min + 100, // slider max + num => this.setBlockTransparency(num), // slider action + 0 // decimals + ); +}; + +IDE_Morph.prototype.setBlockTransparency = function (num, save) { + SyntaxElementMorph.prototype.setAlphaScaled(100 - num); + this.changed(); + if (save) { + if (num === 0) { + this.removeSetting('fade'); + } else { + this.saveSetting('fade', num); + } + } +}; + // IDE_Morph stage size manipulation IDE_Morph.prototype.userSetStageSize = function () { diff --git a/src/morphic.js b/src/morphic.js index 7928cf7b..7eb28ca8 100644 --- a/src/morphic.js +++ b/src/morphic.js @@ -471,6 +471,8 @@ mouseLeave mouseEnterDragging mouseLeaveDragging + mouseEnterBounds + mouseLeaveBounds mouseMove mouseScroll @@ -493,6 +495,8 @@ mouseEnterDragging(morph) mouseLeaveDragging(morph) + mouseEnterBounds(morph) + mouseLeaveBounds(morph) event methods have as optional parameter the morph currently dragged by the Hand, if any. @@ -1276,13 +1280,14 @@ /*global window, HTMLCanvasElement, FileReader, Audio, FileList, Map*/ -var morphicVersion = '2020-July-16'; +var morphicVersion = '2020-July-22'; 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); +const CLEAR = new Color(0, 0, 0, 0); Object.freeze(ZERO); Object.freeze(BLACK); @@ -2304,6 +2309,14 @@ Color.prototype.inverted = function () { ); }; +Color.prototype.solid = function () { + return new Color( + this.r, + this.g, + this.b + ); +}; + // Points ////////////////////////////////////////////////////////////// // Point instance creation: @@ -3467,7 +3480,7 @@ Morph.prototype.getImage = function () { }; Morph.prototype.render = function (aContext) { - aContext.fillStyle = this.color.toString(); + aContext.fillStyle = this.getRenderColor().toString(); aContext.fillRect(0, 0, this.width(), this.height()); if (this.cachedTexture) { this.renderCachedTexture(aContext); @@ -3476,6 +3489,11 @@ Morph.prototype.render = function (aContext) { } }; +Morph.prototype.getRenderColor = function () { + // can be overriden by my heirs or instances + return this.color; +}; + Morph.prototype.fixLayout = function () { // implemented by my heirs // determine my extent and arrange my submorphs, if any @@ -8507,6 +8525,11 @@ StringMorph.prototype.font = function () { this.fontStyle; }; +StringMorph.prototype.getShadowRenderColor = function () { + // answer the shadow rendering color, can be overridden for my children + return this.shadowColor; +}; + StringMorph.prototype.fixLayout = function (justMe) { // determine my extent depending on my current settings var width, @@ -8537,6 +8560,7 @@ StringMorph.prototype.fixLayout = function (justMe) { StringMorph.prototype.render = function (ctx) { var start, stop, i, p, c, x, y, shadowOffset = this.shadowOffset || ZERO, + shadowColor = this.getShadowRenderColor(), txt = this.isPassword ? this.password('*', this.text.length) : this.text; @@ -8546,17 +8570,17 @@ StringMorph.prototype.render = function (ctx) { ctx.textBaseline = 'bottom'; // first draw the shadow, if any - if (this.shadowColor) { + if (shadowColor) { x = Math.max(shadowOffset.x, 0); y = Math.max(shadowOffset.y, 0); - ctx.fillStyle = this.shadowColor.toString(); + ctx.fillStyle = shadowColor.toString(); ctx.fillText(txt, x, fontHeight(this.fontSize) + y); } // now draw the actual text x = Math.abs(Math.min(shadowOffset.x, 0)); y = Math.abs(Math.min(shadowOffset.y, 0)); - ctx.fillStyle = this.color.toString(); + ctx.fillStyle = this.getRenderColor().toString(); if (this.isShowingBlanks) { this.renderWithBlanks( @@ -8615,7 +8639,7 @@ StringMorph.prototype.renderWithBlanks = function (ctx, startX, y) { } isFirst = false; if (word !== '') { - ctx.fillStyle = this.color.toString(); + ctx.fillStyle = this.getRenderColor().toString(); ctx.fillText(word, x, y); x += ctx.measureText(word).width; } @@ -9268,6 +9292,7 @@ TextMorph.prototype.fixLayout = function () { TextMorph.prototype.render = function (ctx) { var shadowWidth = Math.abs(this.shadowOffset.x), shadowHeight = Math.abs(this.shadowOffset.y), + shadowColor = this.getShadowRenderColor(), i, line, width, offx, offy, x, y, start, stop, p, c; // prepare context for drawing text @@ -9282,10 +9307,10 @@ TextMorph.prototype.render = function (ctx) { } // draw the shadow, if any - if (this.shadowColor) { + if (shadowColor) { offx = Math.max(this.shadowOffset.x, 0); offy = Math.max(this.shadowOffset.y, 0); - ctx.fillStyle = this.shadowColor.toString(); + ctx.fillStyle = shadowColor.toString(); for (i = 0; i < this.lines.length; i = i + 1) { line = this.lines[i]; @@ -9306,7 +9331,7 @@ TextMorph.prototype.render = function (ctx) { // now draw the actual text offx = Math.abs(Math.min(this.shadowOffset.x, 0)); offy = Math.abs(Math.min(this.shadowOffset.y, 0)); - ctx.fillStyle = this.color.toString(); + ctx.fillStyle = this.getRenderColor().toString(); for (i = 0; i < this.lines.length; i = i + 1) { line = this.lines[i]; @@ -9336,6 +9361,9 @@ TextMorph.prototype.render = function (ctx) { } }; +TextMorph.prototype.getShadowRenderColor = + StringMorph.prototype.getShadowRenderColor; + TextMorph.prototype.setExtent = function (aPoint) { this.maxWidth = Math.max(aPoint.x, 0); this.changed(); @@ -11066,6 +11094,7 @@ HandMorph.prototype.init = function (aWorld) { this.world = aWorld; this.mouseButton = null; this.mouseOverList = []; + this.mouseOverBounds = []; this.morphToGrab = null; this.grabPosition = null; this.grabOrigin = null; @@ -11120,7 +11149,7 @@ HandMorph.prototype.fullDrawOn = function (ctx, rect) { if (!clipped.extent().gt(ZERO)) {return; } ctx.save(); - ctx.globalAlpha = this.children[0].alpha; + ctx.globalAlpha = this.alpha; pic = this.cachedFullImage; src = clipped.translateBy(pos.neg()); sl = src.left(); @@ -11150,7 +11179,10 @@ HandMorph.prototype.morphAtPointer = function () { HandMorph.prototype.allMorphsAtPointer = function () { return this.world.allChildren().filter(m => m.isVisible && - m.visibleBounds().containsPoint(this.bounds.origin)); + m.visibleBounds().containsPoint(this.bounds.origin) && + !m.holes.some(any => + any.translateBy(m.position()).containsPoint(this.bounds.origin)) + ); }; // HandMorph dragging and dropping: @@ -11201,6 +11233,7 @@ HandMorph.prototype.grab = function (aMorph) { HandMorph.prototype.drop = function () { var target, morphToDrop; + this.alpha = 1; if (this.children.length !== 0) { morphToDrop = this.children[0]; target = this.dropTargetFor(morphToDrop); @@ -11240,13 +11273,23 @@ HandMorph.prototype.drop = function () { mouseLeave mouseEnterDragging mouseLeaveDragging + mouseEnterBounds + mouseLeaveBounds mouseMove mouseScroll */ HandMorph.prototype.processMouseDown = function (event) { - var morph, actualClick; + var morph, actualClick, + posInDocument = getDocumentPositionOf(this.world.worldCanvas); + // update my position, in case I've just been initialized + this.setPosition(new Point( + event.pageX - posInDocument.x, + event.pageY - posInDocument.y + )); + + // process the actual event this.destroyTemporaries(); this.contextMenuEnabled = true; this.morphToGrab = null; @@ -11385,6 +11428,7 @@ HandMorph.prototype.processMouseMove = function (event) { var pos, posInDocument = getDocumentPositionOf(this.world.worldCanvas), mouseOverNew, + mouseOverBoundsNew, morph, topMorph; @@ -11396,8 +11440,12 @@ HandMorph.prototype.processMouseMove = function (event) { this.setPosition(pos); // determine the new mouse-over-list: - // mouseOverNew = this.allMorphsAtPointer(); mouseOverNew = this.morphAtPointer().allParents(); + mouseOverBoundsNew = mouseOverNew.filter(m => m.isVisible && + m.visibleBounds().containsPoint(this.bounds.origin) && + !m.holes.some(any => + any.translateBy(m.position()).containsPoint(this.bounds.origin)) + ); if (!this.children.length && this.mouseButton) { topMorph = this.morphAtPointer(); @@ -11434,6 +11482,21 @@ HandMorph.prototype.processMouseMove = function (event) { } } + this.mouseOverBounds.forEach(old => { + if (!contains(mouseOverBoundsNew, old)) { + if (old.mouseLeaveBounds) { + old.mouseLeaveBounds(this.children[0]); + } + } + }); + mouseOverBoundsNew.forEach(newMorph => { + if (!contains(this.mouseOverBounds, newMorph)) { + if (newMorph.mouseEnterBounds) { + newMorph.mouseEnterBounds(this.children[0]); + } + } + }); + this.mouseOverList.forEach(old => { if (!contains(mouseOverNew, old)) { if (old.mouseLeave) { @@ -11471,6 +11534,7 @@ HandMorph.prototype.processMouseMove = function (event) { } }); this.mouseOverList = mouseOverNew; + this.mouseOverBounds = mouseOverBoundsNew; }; HandMorph.prototype.processMouseScroll = function (event) { @@ -12553,6 +12617,7 @@ WorldMorph.prototype.slide = function (aStringOrTextMorph) { slider.action = (num) => { aStringOrTextMorph.changed(); aStringOrTextMorph.text = Math.round(num).toString(); + aStringOrTextMorph.fixLayout(); aStringOrTextMorph.rerender(); aStringOrTextMorph.escalateEvent( 'reactToSliderEdit', diff --git a/src/objects.js b/src/objects.js index 3a3ce1b0..81dd63fd 100644 --- a/src/objects.js +++ b/src/objects.js @@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph, localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph, HandleMorph, AlignmentMorph, Process, XML_Element, WorldMap, copyCanvas*/ -modules.objects = '2020-July-13'; +modules.objects = '2020-July-20'; var SpriteMorph; var StageMorph; @@ -10965,7 +10965,7 @@ CellMorph.prototype.reactToEdit = function (textMorph) { if (listWatcher) { listWatcher.list.put( textMorph.text, - this.idx + this.idx + listWatcher.start - 1 ); } } diff --git a/src/symbols.js b/src/symbols.js index 3b168a79..05d70958 100644 --- a/src/symbols.js +++ b/src/symbols.js @@ -41,7 +41,7 @@ // Global stuff //////////////////////////////////////////////////////// -modules.symbols = '2020-July-13'; +modules.symbols = '2020-July-21'; var SymbolMorph; @@ -194,6 +194,13 @@ SymbolMorph.prototype.setLabelColor = function ( this.setColor(textColor); }; +// SymbolMorph dynamic coloring: + +SymbolMorph.prototype.getShadowRenderColor = function () { + // answer the shadow rendering color, can be overridden for my children + return this.shadowColor; +}; + // SymbolMorph layout: SymbolMorph.prototype.setExtent = function (aPoint) { @@ -221,12 +228,12 @@ SymbolMorph.prototype.render = function (ctx) { if (this.shadowColor) { ctx.save(); ctx.translate(sx, sy); - this.renderShape(ctx, this.shadowColor); + this.renderShape(ctx, this.getShadowRenderColor()); ctx.restore(); } ctx.save(); ctx.translate(x, y); - this.renderShape(ctx, this.color); + this.renderShape(ctx, this.getRenderColor()); ctx.restore(); }; diff --git a/src/threads.js b/src/threads.js index 98b34b43..adb0b126 100644 --- a/src/threads.js +++ b/src/threads.js @@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy, Map, isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, BLACK, TableFrameMorph, ColorSlotMorph, isSnapObject, newCanvas, Symbol, SVG_Costume*/ -modules.threads = '2020-July-09'; +modules.threads = '2020-July-22'; var ThreadManager; var Process; @@ -6114,15 +6114,15 @@ Context.prototype.image = function () { } } ring.embed(block, this.inputs); - return ring.fullImage(); + return ring.doWithAlpha(1, () => ring.fullImage()); } if (this.expression instanceof Array) { block = this.expression[this.pc].fullCopy(); if (block instanceof RingMorph && !block.contents()) { // empty ring - return block.fullImage(); + return block.doWithAlpha(1, () => block.fullImage()); } ring.embed(block, this.isContinuation ? [] : this.inputs); - return ring.fullImage(); + return ring.doWithAlpha(1, () => ring.fullImage()); } // otherwise show an empty ring @@ -6135,7 +6135,7 @@ Context.prototype.image = function () { ring.parts()[1].addInput(inp) ); } - return ring.fullImage(); + return ring.doWithAlpha(1, () => ring.fullImage()); }; // Context continuations: diff --git a/src/widgets.js b/src/widgets.js index 641b9a00..21a1a99b 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -85,7 +85,7 @@ HTMLCanvasElement, fontHeight, SymbolMorph, localize, SpeechBubbleMorph, ArrowMorph, MenuMorph, isString, isNil, SliderMorph, MorphicPreferences, ScrollFrameMorph, MenuItemMorph, Note*/ -modules.widgets = '2020-July-13'; +modules.widgets = '2020-July-21'; var PushButtonMorph; var ToggleButtonMorph; @@ -728,7 +728,7 @@ ToggleButtonMorph.prototype.render = function (ctx) { // note: don't invert the 3D-ish edges for 'pressed' state, because // it will stay that way, and should not look inverted (or should it?) this.drawOutline(ctx); - this.drawBackground(ctx, this.pressColor); + this.drawBackground(ctx, this.getPressRenderColor()); this.drawEdges( ctx, this.pressColor, @@ -748,6 +748,11 @@ ToggleButtonMorph.prototype.render = function (ctx) { } }; +ToggleButtonMorph.prototype.getPressRenderColor = function () { + // can be overridden by my children + return this.pressColor; +}; + ToggleButtonMorph.prototype.drawEdges = function ( ctx, color, @@ -1597,10 +1602,12 @@ DialogBoxMorph.prototype.prompt = function ( isNumeric, // optional sliderMin, // optional for numeric sliders sliderMax, // optional for numeric sliders - sliderAction // optional single-arg function for numeric slider + sliderAction, // optional single-arg function for numeric slider + decimals = 2 // optional number of decimal digits ) { var sld, head, + precision = Math.pow(10, decimals), txt = new InputFieldMorph( defaultString, isNumeric || false, // numeric? @@ -1616,10 +1623,10 @@ DialogBoxMorph.prototype.prompt = function ( } if (!isNil(sliderMin) && !isNil(sliderMax)) { sld = new SliderMorph( - sliderMin * 100, - sliderMax * 100, - parseFloat(defaultString) * 100, - (sliderMax - sliderMin) / 10 * 100, + sliderMin * precision, + sliderMax * precision, + parseFloat(defaultString) * precision, + (sliderMax - sliderMin) / 10 * precision, // knob size 'horizontal' ); sld.alpha = 1; @@ -1628,9 +1635,9 @@ DialogBoxMorph.prototype.prompt = function ( sld.setWidth(txt.width()); sld.action = num => { if (sliderAction) { - sliderAction(num / 100); + sliderAction(num / precision); } - txt.setContents(num / 100); + txt.setContents(num / precision); txt.edit(); }; if (!head) { @@ -1649,7 +1656,7 @@ DialogBoxMorph.prototype.prompt = function ( this.reactToChoice = function (inp) { if (sld) { - sld.value = inp * 100; + sld.value = inp * precision; sld.fixLayout(); sld.rerender(); } @@ -1662,7 +1669,7 @@ DialogBoxMorph.prototype.prompt = function ( var inp = txt.getValue(); if (sld) { inp = Math.max(inp, sliderMin); - sld.value = inp * 100; + sld.value = inp * precision; sld.fixLayout(); sld.rerender(); }