diff --git a/HISTORY.md b/HISTORY.md
index 68fc14e5..cfd53967 100755
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -50,6 +50,7 @@
### 2022-05-017
* blocks: added experimental private isChangeableTo(type) method
+* blocks, threads: tweaked programmatic blocks-changing
### 2022-05-06
* threads: include currently dragged sprites in the MY OTHER SPRITES/CLONES lists
diff --git a/snap.html b/snap.html
index 14f354ea..eb514ffd 100755
--- a/snap.html
+++ b/snap.html
@@ -17,7 +17,7 @@
-
+
diff --git a/src/blocks.js b/src/blocks.js
index 50d453a2..0ac0c9d2 100644
--- a/src/blocks.js
+++ b/src/blocks.js
@@ -3577,28 +3577,19 @@ BlockMorph.prototype.isChangeableTo = function (type) {
// a block is considered "changeable" if
// -------------------------------------
// * it's a command & the target type isn't also a command & doesn't have a
- // next block & its parent also isn't a syntax element (block or C-shaped
- // slot), except it can be a ring.
+ // next block & is unattached (e.g. the only expression inside a context).
//
- // * it's a reporter or a predicate & the target type is a command & its
- // parent is not a block, except it can be a ring.
+ // * it's a reporter or a predicate & the target type is a command & is
+ // unattached (e.g. the only expression inside a function context).
//
// * it's a reporter or a predicate & the target type is also a reporter or
// a predicate the type can always be changed
var typ = this.type();
if (typ === type) {return true; }
- if (typ === 'command') {
- if (this.nextBlock()) {return false; }
- if (this.parent instanceof RingMorph) {return true; }
- return !(this.parent instanceof SyntaxElementMorph);
+ if (typ === 'command' || type === 'command') {
+ return this.isUnattached();
}
- // typ is 'reporter' or 'predicate'
- if (type === 'command') {
- if (this.parent instanceof RingMorph) {return true; }
- return !(this.parent instanceof BlockMorph);
- }
- // type is 'reporter' or 'predicate'
return true;
};
@@ -3608,6 +3599,13 @@ BlockMorph.prototype.type = function () {
: (this.isPredicate ? 'predicate' : 'reporter');
};
+BlockMorph.prototype.isUnattached = function () {
+ // private
+ return ((this.nextBlock && !this.nextBlock()) || !this.nextBlock) &&
+ !(this.parent instanceof SyntaxElementMorph) &&
+ !(this.parent instanceof ScriptsMorph)
+};
+
BlockMorph.prototype.isInheritedVariable = function (shadowedOnly) {
// private - only for variable getter template inside the palette
if (this.isTemplate &&
diff --git a/src/threads.js b/src/threads.js
index 6a26bedc..e1df3c62 100644
--- a/src/threads.js
+++ b/src/threads.js
@@ -65,7 +65,7 @@ StagePickerMorph, CustomBlockDefinition*/
/*jshint esversion: 11, bitwise: false, evil: true*/
-modules.threads = '2022-May-06';
+modules.threads = '2022-May-17';
var ThreadManager;
var Process;
@@ -5687,9 +5687,6 @@ Process.prototype.doSetBlockAttribute = function (attribute, block, val) {
'other';
break;
case 'type':
- if (isInUse()) {
- throw new Error('cannot change this\nfor a block that is in use');
- }
this.assertType(val, ['number', 'text']);
if (this.reportTypeOf(val) === 'text') {
type = val;
@@ -5697,7 +5694,14 @@ Process.prototype.doSetBlockAttribute = function (attribute, block, val) {
type = types[val - 1] || '';
}
if (!types.includes(type)) {return;}
- def.type = type;
+
+ if (rcvr.allBlockInstances(def).every(block =>
+ block.isChangeableTo(type))
+ ) {
+ def.type = type;
+ } else {
+ throw new Error('cannot change this\nfor a block that is in use');
+ }
break;
case 'scope':
if (isInUse()) {