kopia lustrzana https://github.com/backface/turtlestitch
new menu functionality for ASK command, when passing a list
rodzic
ed15709f1e
commit
12a2739cc1
|
@ -2,6 +2,7 @@
|
|||
|
||||
## in development:
|
||||
* **New Features:**
|
||||
* passing a list to the ASK command in sensing presents a menu to the user
|
||||
* export script (including dependencies) via its context menu
|
||||
* export / import sprite-local custom block definitions from the palette
|
||||
* added "combinations" primitive to the palette
|
||||
|
@ -26,6 +27,9 @@
|
|||
* **Translation Updates:**
|
||||
* German
|
||||
|
||||
### 2022-03-31
|
||||
* threads, objects: new menu functionality for ASK command, when passing a list
|
||||
|
||||
### 2022-03-28
|
||||
* new "Tad" turtle costumes, thanks, Meghan and Brian!
|
||||
* blocks, threads: new "position" choice in OF reporter's attribute dropdown, reports a list of XY coordinates
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<script src="src/symbols.js?version=2021-03-03"></script>
|
||||
<script src="src/widgets.js?version=2021-17-09"></script>
|
||||
<script src="src/blocks.js?version=2022-03-28"></script>
|
||||
<script src="src/threads.js?version=2022-03-28"></script>
|
||||
<script src="src/objects.js?version=2022-03-16"></script>
|
||||
<script src="src/threads.js?version=2022-03-31"></script>
|
||||
<script src="src/objects.js?version=2022-03-31"></script>
|
||||
<script src="src/scenes.js?version=2022-03-03"></script>
|
||||
<script src="src/gui.js?version=2022-03-17"></script>
|
||||
<script src="src/paint.js?version=2021-07-05"></script>
|
||||
|
|
410
src/objects.js
410
src/objects.js
|
@ -49,7 +49,13 @@
|
|||
CellMorph
|
||||
WatcherMorph
|
||||
StagePrompterMorph
|
||||
StagePickerMorph
|
||||
StagePickerItemMorph
|
||||
|
||||
MenuItemMorph*
|
||||
StagePickerItemMorph
|
||||
MenuMorph*
|
||||
StagePickerMorph
|
||||
SpeechBubbleMorph*
|
||||
SpriteBubbleMorph
|
||||
|
||||
|
@ -83,11 +89,11 @@ SpeechBubbleMorph, InputSlotMorph, isNil, FileReader, TableDialogMorph, String,
|
|||
BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, BooleanSlotMorph,
|
||||
localize, TableMorph, TableFrameMorph, normalizeCanvas, VectorPaintEditorMorph,
|
||||
AlignmentMorph, Process, WorldMap, copyCanvas, useBlurredShadows,
|
||||
BlockVisibilityDialogMorph, CostumeIconMorph, SoundIconMorph*/
|
||||
BlockVisibilityDialogMorph, CostumeIconMorph, SoundIconMorph, MenuItemMorph*/
|
||||
|
||||
/*jshint esversion: 6*/
|
||||
|
||||
modules.objects = '2022-March-18';
|
||||
modules.objects = '2022-March-31';
|
||||
|
||||
var SpriteMorph;
|
||||
var StageMorph;
|
||||
|
@ -103,6 +109,8 @@ var WatcherMorph;
|
|||
var StagePrompterMorph;
|
||||
var Note;
|
||||
var SpriteHighlightMorph;
|
||||
var StagePickerMorph;
|
||||
var StagePickerItemMorph;
|
||||
|
||||
function isSnapObject(thing) {
|
||||
return thing instanceof SpriteMorph || (thing instanceof StageMorph);
|
||||
|
@ -8176,6 +8184,9 @@ StageMorph.prototype.setScale = function (number) {
|
|||
}
|
||||
morph.setCenter(this.center());
|
||||
morph.setBottom(this.bottom());
|
||||
} else if (morph instanceof StagePickerMorph) {
|
||||
morph.createItems(number);
|
||||
morph.popup(this, morph.position());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -12727,6 +12738,7 @@ StagePrompterMorph.prototype.init = function (question) {
|
|||
// question is optional in case the Stage is asking
|
||||
|
||||
// additional properties
|
||||
this.answer = null;
|
||||
this.isDone = false;
|
||||
if (question) {
|
||||
this.label = new StringMorph(
|
||||
|
@ -12800,5 +12812,399 @@ StagePrompterMorph.prototype.mouseClickLeft = function () {
|
|||
};
|
||||
|
||||
StagePrompterMorph.prototype.accept = function () {
|
||||
this.answer = this.inputField.getValue();
|
||||
this.isDone = true;
|
||||
};
|
||||
|
||||
// StagePickerMorph ////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
I am a sensor-category-colored input box which lets the user pick one
|
||||
from a list of options.
|
||||
*/
|
||||
|
||||
// StagePickerMorph inherits from MenuMorph:
|
||||
|
||||
StagePickerMorph.prototype = new MenuMorph();
|
||||
StagePickerMorph.prototype.constructor = StagePickerMorph;
|
||||
StagePickerMorph.uber = MenuMorph.prototype;
|
||||
|
||||
// StagePickerMorph instance creation:
|
||||
|
||||
function StagePickerMorph(options) {
|
||||
this.init(options);
|
||||
}
|
||||
|
||||
StagePickerMorph.prototype.init = function (options) {
|
||||
var first = options.at(1),
|
||||
isSubmenu = this.isSubmenu(options),
|
||||
title = isSubmenu ? first : null,
|
||||
items = isSubmenu ? options.at(2) : options;
|
||||
|
||||
// additional properties
|
||||
this.answer = null;
|
||||
this.isDone = false;
|
||||
this.scale = 1;
|
||||
|
||||
// initialize inherited properties
|
||||
StagePickerMorph.uber.init.call(
|
||||
this,
|
||||
choice => {
|
||||
var root = this.rootMenu();
|
||||
root.answer = choice;
|
||||
root.isDone = true;
|
||||
},
|
||||
title, // title
|
||||
this, // environment
|
||||
null // font size
|
||||
);
|
||||
|
||||
// override inherited behavior
|
||||
|
||||
// create items
|
||||
items.map(each => {
|
||||
var key, value, isLine;
|
||||
if (this.isSubmenu(each)) {
|
||||
this.addMenu(
|
||||
each.at(1), // label
|
||||
new StagePickerMorph(each.at(2)), // aMenu
|
||||
null, // indicator
|
||||
true // verbatim, don't translate
|
||||
);
|
||||
} else {
|
||||
key = each;
|
||||
value = each;
|
||||
if (each instanceof List) { // treat as pair
|
||||
isLine = each.isEmpty();
|
||||
key = each.at(1);
|
||||
value = each.at(2);
|
||||
}
|
||||
if (isLine) {
|
||||
this.addLine();
|
||||
} else {
|
||||
this.addItem(
|
||||
this.dataRepresentation(key),
|
||||
value,
|
||||
null, // hint
|
||||
null, // color
|
||||
null, // bold
|
||||
null, // italic
|
||||
null, // doubleClickAction
|
||||
null, // shortcut
|
||||
true // verbatim? don't translate
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
StagePickerMorph.prototype.isSubmenu = function (options) {
|
||||
var first;
|
||||
if (!(options instanceof List)) {
|
||||
return false;
|
||||
}
|
||||
first = options.at(1);
|
||||
return isString(first) &&
|
||||
first.length &&
|
||||
options.length() === 2 &&
|
||||
options.rank() > 1;
|
||||
};
|
||||
|
||||
StagePickerMorph.prototype.dataRepresentation = function (data) {
|
||||
switch (Process.prototype.reportTypeOf(data)) {
|
||||
case 'number':
|
||||
return data.toString();
|
||||
default:
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
// StagePickerMorph popping up
|
||||
|
||||
StagePickerMorph.prototype.popup = function (stage, pos) {
|
||||
var scroller;
|
||||
|
||||
this.setPosition(pos);
|
||||
this.keepWithin(stage);
|
||||
|
||||
if (this.bottom() > stage.bottom()) {
|
||||
// scroll menu items if the menu is taller than the stage
|
||||
scroller = this.scroll();
|
||||
this.bounds.corner.y = stage.bottom() - 2;
|
||||
scroller.setHeight(stage.bottom() - scroller.top() - this.edge - 2);
|
||||
scroller.adjustScrollBars();
|
||||
}
|
||||
|
||||
if (this.items.length < 1 && !this.title) { // don't show empty menus
|
||||
return;
|
||||
}
|
||||
stage.add(this);
|
||||
this.fullChanged();
|
||||
};
|
||||
|
||||
StagePickerMorph.prototype.createLabel = function () {
|
||||
var text;
|
||||
if (this.label !== null) {
|
||||
this.label.destroy();
|
||||
}
|
||||
text = new TextMorph(
|
||||
localize(this.title),
|
||||
SpriteMorph.prototype.bubbleFontSize * this.scale,
|
||||
null, // MorphicPreferences.menuFontName,
|
||||
true,
|
||||
false,
|
||||
'center'
|
||||
);
|
||||
text.alignment = 'center';
|
||||
text.color = WHITE;
|
||||
text.backgroundColor = this.borderColor;
|
||||
text.fixLayout();
|
||||
this.label = new BoxMorph(this.edge, 0);
|
||||
this.label.color = this.borderColor;
|
||||
this.label.borderColor = this.borderColor;
|
||||
|
||||
this.label.outlinePath = function (ctx, corner, inset) {
|
||||
// modify to only draw the top corners rounded
|
||||
var w = this.width(),
|
||||
h = this.height(),
|
||||
radius = Math.min(corner, (Math.min(w, h) - inset) / 2),
|
||||
offset = radius + inset;
|
||||
|
||||
// top left:
|
||||
ctx.arc(
|
||||
offset,
|
||||
offset,
|
||||
radius,
|
||||
radians(-180),
|
||||
radians(-90),
|
||||
false
|
||||
);
|
||||
|
||||
// top right:
|
||||
ctx.arc(
|
||||
w - offset,
|
||||
offset,
|
||||
radius,
|
||||
radians(-90),
|
||||
radians(-0),
|
||||
false
|
||||
);
|
||||
|
||||
// bottom right:
|
||||
ctx.lineTo(w, h);
|
||||
|
||||
// bottom left:
|
||||
ctx.lineTo(0, h);
|
||||
};
|
||||
|
||||
this.label.setExtent(text.extent().add(this.edge));
|
||||
this.label.add(text);
|
||||
this.label.text = text;
|
||||
};
|
||||
|
||||
StagePickerMorph.prototype.createItems = function (scale) {
|
||||
var item,
|
||||
x,
|
||||
y,
|
||||
isLine = false;
|
||||
|
||||
this.scale = scale;
|
||||
this.children.forEach(m => m.destroy());
|
||||
this.children = [];
|
||||
this.edge = SyntaxElementMorph.prototype.rounding * this.scale;
|
||||
this.border = SpriteMorph.prototype.bubbleBorder * this.scale;
|
||||
this.color = WHITE;
|
||||
this.borderColor = SpriteMorph.prototype.blockColor.sensing;
|
||||
this.setExtent(new Point(0, 0));
|
||||
|
||||
y = this.border;
|
||||
x = this.left() + this.border;
|
||||
if (this.title) {
|
||||
this.createLabel();
|
||||
this.label.setPosition(this.bounds.origin);
|
||||
this.add(this.label);
|
||||
y = this.label.bottom();
|
||||
} else {
|
||||
y = this.top() + this.edge;
|
||||
}
|
||||
y += 1;
|
||||
this.items.forEach(tuple => {
|
||||
isLine = false;
|
||||
if (tuple[0] === 0) {
|
||||
isLine = true;
|
||||
item = new Morph();
|
||||
item.color = this.borderColor;
|
||||
item.setHeight(tuple[1] * this.scale);
|
||||
} else {
|
||||
item = new StagePickerItemMorph(
|
||||
this.target,
|
||||
tuple[1],
|
||||
tuple[0],
|
||||
SpriteMorph.prototype.bubbleFontSize * this.scale,
|
||||
null, // MorphicPreferences.menuFontName,
|
||||
this.environment,
|
||||
tuple[2], // bubble help hint
|
||||
tuple[3], // color
|
||||
tuple[4], // bold
|
||||
tuple[5], // italic
|
||||
tuple[6], // doubleclick action
|
||||
tuple[7] // shortcut
|
||||
);
|
||||
}
|
||||
if (isLine) {
|
||||
y += 1;
|
||||
}
|
||||
item.setPosition(new Point(x, y));
|
||||
this.add(item);
|
||||
y = y + item.height();
|
||||
if (isLine) {
|
||||
y += 1;
|
||||
}
|
||||
});
|
||||
|
||||
this.adjustWidths();
|
||||
this.setExtent(
|
||||
this.fullBounds().extent().add(new Point(this.border, this.edge))
|
||||
);
|
||||
if (this.label) {
|
||||
this.label.setWidth(this.width());
|
||||
this.label.text.setPosition(
|
||||
this.label.center().subtract(
|
||||
this.label.text.extent().floorDivideBy(2)
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
StagePickerMorph.prototype.maxWidth = function () {
|
||||
var w = 0;
|
||||
|
||||
if (this.parent instanceof FrameMorph) {
|
||||
if (this.parent.scrollFrame instanceof ScrollFrameMorph) {
|
||||
w = this.parent.scrollFrame.width();
|
||||
}
|
||||
}
|
||||
this.children.forEach(item => {
|
||||
if (item instanceof MenuItemMorph) {
|
||||
w = Math.max(
|
||||
w,
|
||||
item.label.width() + this.edge +
|
||||
(item.shortcut ? item.shortcut.width() + this.border : 0)
|
||||
);
|
||||
}
|
||||
});
|
||||
if (this.label) {
|
||||
w = Math.max(w, this.label.width() - this.border);
|
||||
}
|
||||
return w;
|
||||
};
|
||||
|
||||
StagePickerMorph.prototype.adjustWidths = function () {
|
||||
var w = this.maxWidth();
|
||||
this.children.forEach(item => {
|
||||
item.setWidth(w);
|
||||
item.fixLayout();
|
||||
});
|
||||
};
|
||||
|
||||
// StagePickerMorph removing
|
||||
|
||||
StagePickerMorph.prototype.destroy = function () {
|
||||
MenuMorph.uber.destroy.call(this);
|
||||
};
|
||||
|
||||
// StagePickerMorph submenus
|
||||
|
||||
StagePickerMorph.prototype.closeSubmenu = function () {
|
||||
if (this.submenu) {
|
||||
this.submenu.destroy();
|
||||
this.submenu = null;
|
||||
this.unselectAllItems();
|
||||
}
|
||||
};
|
||||
|
||||
StagePickerMorph.prototype.rootMenu = function () {
|
||||
return (this.parent instanceof StagePickerMorph) ?
|
||||
this.parent.rootMenu()
|
||||
: this;
|
||||
};
|
||||
|
||||
// StagePickerItemMorph ////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
I am an option that can be clicked inside a StagePickerMorph.
|
||||
*/
|
||||
|
||||
// StagePickerItemMorph inherits from MenuItemMorph:
|
||||
|
||||
StagePickerItemMorph.prototype = new MenuItemMorph();
|
||||
StagePickerItemMorph.prototype.constructor = StagePickerItemMorph;
|
||||
StagePickerItemMorph.uber = MenuItemMorph.prototype;
|
||||
|
||||
// StagePickerItemMorph instance creation:
|
||||
|
||||
function StagePickerItemMorph(
|
||||
target,
|
||||
action,
|
||||
labelString, // can also be a Morph or a Canvas or a tuple: [icon, string]
|
||||
fontSize,
|
||||
fontStyle,
|
||||
environment,
|
||||
hint,
|
||||
color,
|
||||
bold,
|
||||
italic,
|
||||
doubleClickAction, // optional when used as list morph item
|
||||
shortcut // optional string, Morph, Canvas or tuple: [icon, string]
|
||||
) {
|
||||
this.shortcutString = shortcut || null;
|
||||
this.shortcut = null;
|
||||
this.init(
|
||||
target,
|
||||
action,
|
||||
labelString,
|
||||
fontSize,
|
||||
fontStyle,
|
||||
environment,
|
||||
hint,
|
||||
color,
|
||||
bold,
|
||||
italic,
|
||||
doubleClickAction
|
||||
);
|
||||
|
||||
this.highlightColor = SpriteMorph.prototype.blockColor.sensing.lighter(75);
|
||||
this.pressColor = SpriteMorph.prototype.blockColor.sensing.lighter(25);
|
||||
if (this.shortcut) {
|
||||
this.shortcut.setColor(SpriteMorph.prototype.blockColor.sensing);
|
||||
}
|
||||
}
|
||||
|
||||
// StagePickerItemMorph submenus:
|
||||
|
||||
StagePickerItemMorph.prototype.popUpSubmenu = function () {
|
||||
var menu = this.parentThatIsA(MenuMorph),
|
||||
stage = this.parentThatIsA(StageMorph),
|
||||
scroller;
|
||||
|
||||
if (!(this.action instanceof MenuMorph)) {return; }
|
||||
this.action.createItems(menu.scale);
|
||||
this.action.setPosition(this.topRight().subtract(new Point(0, 5)));
|
||||
this.action.keepWithin(stage);
|
||||
if (this.action.items.length < 1 && !this.action.title) {return; }
|
||||
|
||||
if (this.action.bottom() > stage.bottom()) {
|
||||
// scroll menu items if the menu is taller than the world
|
||||
scroller = this.action.scroll();
|
||||
this.action.bounds.corner.y = stage.bottom() - 2;
|
||||
scroller.setHeight(
|
||||
stage.bottom() - scroller.top() - this.action.edge - 2
|
||||
);
|
||||
scroller.adjustScrollBars();
|
||||
}
|
||||
|
||||
menu.add(this.action);
|
||||
menu.submenu = this.action;
|
||||
this.action.fullChanged();
|
||||
};
|
||||
|
|
|
@ -60,11 +60,12 @@ IDE_Morph, ArgLabelMorph, localize, XML_Element, hex_sha512, TableDialogMorph,
|
|||
StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy, Map,
|
||||
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, BLACK,
|
||||
TableFrameMorph, ColorSlotMorph, isSnapObject, newCanvas, Symbol, SVG_Costume,
|
||||
SnapExtensions, AlignmentMorph, TextMorph, Cloud, HatBlockMorph*/
|
||||
SnapExtensions, AlignmentMorph, TextMorph, Cloud, HatBlockMorph,
|
||||
StagePickerMorph*/
|
||||
|
||||
/*jshint esversion: 6, bitwise: false*/
|
||||
/*jshint esversion: 6, bitwise: false, evil: true*/
|
||||
|
||||
modules.threads = '2022-March-28';
|
||||
modules.threads = '2022-March-31';
|
||||
|
||||
var ThreadManager;
|
||||
var Process;
|
||||
|
@ -3641,36 +3642,67 @@ Process.prototype.doAsk = function (data) {
|
|||
rcvr = this.blockReceiver(),
|
||||
isStage = rcvr instanceof StageMorph,
|
||||
isHiddenSprite = rcvr instanceof SpriteMorph && !rcvr.isVisible,
|
||||
activePrompter;
|
||||
activePrompter,
|
||||
leftSpace,
|
||||
rightSpace;
|
||||
|
||||
stage.keysPressed = {};
|
||||
if (!this.prompter) {
|
||||
activePrompter = detect(
|
||||
stage.children,
|
||||
morph => morph instanceof StagePrompterMorph
|
||||
morph => morph instanceof StagePrompterMorph ||
|
||||
morph instanceof StagePickerMorph
|
||||
);
|
||||
if (!activePrompter) {
|
||||
if (!isStage && !isHiddenSprite) {
|
||||
rcvr.bubble(data, false, true);
|
||||
}
|
||||
this.prompter = new StagePrompterMorph(
|
||||
isStage || isHiddenSprite ? data : null
|
||||
);
|
||||
if (stage.scale < 1) {
|
||||
this.prompter.setWidth(stage.width() - 10);
|
||||
if (data instanceof List) {
|
||||
if (!isStage) {
|
||||
rcvr.stopTalking();
|
||||
}
|
||||
this.prompter = new StagePickerMorph(data);
|
||||
this.prompter.createItems(stage.scale);
|
||||
leftSpace = rcvr.left() - stage.left();
|
||||
rightSpace = stage.right() - rcvr.right();
|
||||
if (isStage) {
|
||||
this.prompter.popup(
|
||||
stage,
|
||||
stage.center().subtract(
|
||||
this.prompter.extent().floorDivideBy(2)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
this.prompter.popup(
|
||||
stage,
|
||||
rightSpace > this.prompter.width() ||
|
||||
rightSpace >= leftSpace ?
|
||||
rcvr.topRight()
|
||||
: rcvr.topLeft().subtract(
|
||||
new Point(this.prompter.width(), 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.prompter.setWidth(stage.dimensions.x - 20);
|
||||
if (!isStage && !isHiddenSprite) {
|
||||
rcvr.bubble(data, false, true);
|
||||
}
|
||||
this.prompter = new StagePrompterMorph(
|
||||
isStage || isHiddenSprite ? data : null
|
||||
);
|
||||
if (stage.scale < 1) {
|
||||
this.prompter.setWidth(stage.width() - 10);
|
||||
} else {
|
||||
this.prompter.setWidth(stage.dimensions.x - 20);
|
||||
}
|
||||
this.prompter.fixLayout();
|
||||
this.prompter.setCenter(stage.center());
|
||||
this.prompter.setBottom(stage.bottom() - this.prompter.border);
|
||||
stage.add(this.prompter);
|
||||
this.prompter.inputField.edit();
|
||||
stage.changed();
|
||||
}
|
||||
this.prompter.fixLayout();
|
||||
this.prompter.setCenter(stage.center());
|
||||
this.prompter.setBottom(stage.bottom() - this.prompter.border);
|
||||
stage.add(this.prompter);
|
||||
this.prompter.inputField.edit();
|
||||
stage.changed();
|
||||
}
|
||||
} else {
|
||||
if (this.prompter.isDone) {
|
||||
stage.lastAnswer = this.prompter.inputField.getValue();
|
||||
stage.lastAnswer = this.prompter.answer;
|
||||
this.prompter.destroy();
|
||||
this.prompter = null;
|
||||
if (!isStage) {rcvr.stopTalking(); }
|
||||
|
@ -7586,9 +7618,7 @@ JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) {
|
|||
block += value + '=0,';
|
||||
}
|
||||
});
|
||||
/*jshint evil: true*/
|
||||
return Function('...p', block + code);
|
||||
/*jshint evil: false*/
|
||||
};
|
||||
|
||||
JSCompiler.prototype.compileExpression = function (block) {
|
||||
|
|
Ładowanie…
Reference in New Issue