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