summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjmoenig <jens@moenig.org>2013-11-12 11:44:39 +0100
committerjmoenig <jens@moenig.org>2013-11-12 11:44:39 +0100
commit651f44ebdfe3c6ff33f9135b23b971293a211dbc (patch)
treefc809de66ca1b78aa8d3bee9566f4760b9b5ceb8
parentcc7a1558f1aeb3fa0e75378f8688cdfa40c8fb8e (diff)
downloadsnap-yow-651f44ebdfe3c6ff33f9135b23b971293a211dbc.tar.gz
snap-yow-651f44ebdfe3c6ff33f9135b23b971293a211dbc.zip
customizable drop-down menus for input slots
-rw-r--r--blocks.js20
-rw-r--r--byob.js115
-rwxr-xr-xhistory.txt10
-rw-r--r--store.js22
4 files changed, 106 insertions, 61 deletions
diff --git a/blocks.js b/blocks.js
index 6f719d9..1d46b6f 100644
--- a/blocks.js
+++ b/blocks.js
@@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/
// Global stuff ////////////////////////////////////////////////////////
-modules.blocks = '2013-October-25';
+modules.blocks = '2013-November-12';
var SyntaxElementMorph;
var BlockMorph;
@@ -517,6 +517,12 @@ SyntaxElementMorph.prototype.revertToDefaultInput = function (arg, noValues) {
if (idx !== -1) {
if (this instanceof BlockMorph) {
deflt = this.labelPart(this.parseSpec(this.blockSpec)[idx]);
+ if (deflt instanceof InputSlotMorph && this.definition) {
+ deflt.choices = this.definition.dropDownMenuOfInputIdx(
+ this.inputs().indexOf(arg)
+ );
+ deflt.fixLayout();
+ }
} else if (this instanceof MultiArgMorph) {
deflt = this.labelPart(this.slotSpec);
} else if (this instanceof ReporterSlotMorph) {
@@ -1889,7 +1895,8 @@ BlockMorph.prototype.parseSpec = function (spec) {
BlockMorph.prototype.setSpec = function (spec) {
var myself = this,
- part;
+ part,
+ inputIdx = -1;
if (!spec) {return; }
this.parts().forEach(function (part) {
@@ -1899,6 +1906,9 @@ BlockMorph.prototype.setSpec = function (spec) {
this.add(this.placeHolder());
}
this.parseSpec(spec).forEach(function (word) {
+ if (word[0] === '%') {
+ inputIdx += 1;
+ }
part = myself.labelPart(word);
myself.add(part);
if (!(part instanceof CommandSlotMorph)) {
@@ -1917,6 +1927,10 @@ BlockMorph.prototype.setSpec = function (spec) {
if (myself.isPrototype) {
myself.add(myself.placeHolder());
}
+ if (part instanceof InputSlotMorph && myself.definition) {
+ part.choices = myself.definition.dropDownMenuOfInputIdx(inputIdx);
+ part.fixLayout(); // needed when de-serializing
+ }
});
this.blockSpec = spec;
this.fixLayout();
@@ -2325,7 +2339,7 @@ BlockMorph.prototype.mapToHeader = function () {
} else {
help = 'Enter code that corresponds to the block\'s definition. ' +
'Choose your own\nformal parameter names (ignoring the ones ' +
- 'shown .';
+ 'shown).';
}
new DialogBoxMorph(
this,
diff --git a/byob.js b/byob.js
index 855b619..d282b07 100644
--- a/byob.js
+++ b/byob.js
@@ -102,11 +102,11 @@ ArrowMorph, PushButtonMorph, contains, InputSlotMorph, ShadowMorph,
ToggleButtonMorph, IDE_Morph, MenuMorph, copy, ToggleElementMorph,
Morph, fontHeight, StageMorph, SyntaxElementMorph, SnapSerializer,
CommentMorph, localize, CSlotMorph, SpeechBubbleMorph, MorphicPreferences,
-SymbolMorph*/
+SymbolMorph, isNil*/
// Global stuff ////////////////////////////////////////////////////////
-modules.byob = '2013-November-04';
+modules.byob = '2013-November-12';
// Declarations
@@ -137,7 +137,7 @@ function CustomBlockDefinition(spec, receiver) {
this.isGlobal = false;
this.type = 'command';
this.spec = spec || '';
- this.declarations = {}; // {'inputName' : [type, default]}
+ this.declarations = {}; // {'inputName' : [type, default, options]}
this.comment = null;
this.codeMapping = null; // experimental, generate text code
this.codeHeader = null; // experimental, generate text code
@@ -192,6 +192,7 @@ CustomBlockDefinition.prototype.prototypeInstance = function () {
if (slot) {
part.fragment.type = slot[0];
part.fragment.defaultValue = slot[1];
+ part.fragment.options = slot[2];
}
}
});
@@ -265,6 +266,23 @@ CustomBlockDefinition.prototype.defaultValueOfInputIdx = function (idx) {
return this.defaultValueOf(inputName);
};
+CustomBlockDefinition.prototype.dropDownMenuOfInputIdx = function (idx) {
+ var inputName = this.inputNames()[idx];
+ return this.dropDownMenuOf(inputName);
+};
+
+CustomBlockDefinition.prototype.dropDownMenuOf = function (inputName) {
+ var dict = {};
+ if (this.declarations[inputName] && this.declarations[inputName][2]) {
+ this.declarations[inputName][2].split('\n').forEach(function (line) {
+ var pair = line.split('=');
+ dict[pair[0]] = isNil(pair[1]) ? pair[0] : pair[1];
+ });
+ return dict;
+ }
+ return null;
+};
+
CustomBlockDefinition.prototype.inputNames = function () {
var vNames = [],
parts = this.parseSpec(this.spec);
@@ -334,6 +352,13 @@ CustomCommandBlockMorph.prototype.refresh = function () {
this.fixBlockColor();
this.fixLabelColor();
this.restoreInputs(oldInputs);
+ } else { // update all input slots' drop-downs
+ this.inputs().forEach(function (inp, i) {
+ if (inp instanceof InputSlotMorph) {
+ inp.choices = def.dropDownMenuOfInputIdx(i);
+ inp.fixLayout();
+ }
+ });
}
// find unnahmed upvars and label them
@@ -383,48 +408,6 @@ CustomCommandBlockMorph.prototype.refreshDefaults = function () {
});
};
-/*
-custom drop down menus, still incomplete, commented out for now
-
-CustomCommandBlockMorph.prototype.refreshDefaults = function () {
- // fill my editable slots with the defaults specified in my definition
- var inputs = this.inputs(), idx = 0, myself = this, dflt;
-
- inputs.forEach(function (inp) {
- if (inp instanceof InputSlotMorph) {
- dflt = myself.parseDefault(
- myself.definition.defaultValueOfInputIdx(idx)
- );
- inp.choices = dflt.menu;
- inp.setContents(dflt.value);
- }
- idx += 1;
- });
-};
-
-CustomCommandBlockMorph.prototype.parseDefault = function (str) {
- // experimental shot at custom drop downs for input slots,
- // answer an object of form: {value: 'bar', menu: {key: val, ...}}
- var ans = {},
- menu = {},
- tokens;
- if (str.indexOf('&') !== -1) {
- tokens = str.split('&');
- ans.value = tokens[0];
- if (tokens[1]) {
- tokens[1].split(',').forEach(function (entry) {
- var pair = entry.split('=');
- if (pair[0]) {
- menu[pair[0]] = pair[1] || pair[0];
- }
- });
- ans.menu = menu;
- }
- }
- return ans;
-};
-*/
-
CustomCommandBlockMorph.prototype.refreshPrototype = function () {
// create my label parts from my (edited) fragments only
var hat,
@@ -579,8 +562,11 @@ CustomCommandBlockMorph.prototype.declarationsFromFragments = function () {
this.parts().forEach(function (part) {
if (part instanceof BlockInputFragmentMorph) {
- ans[part.fragment.labelString] =
- [part.fragment.type, part.fragment.defaultValue];
+ ans[part.fragment.labelString] = [
+ part.fragment.type,
+ part.fragment.defaultValue,
+ part.fragment.options
+ ];
}
});
return ans;
@@ -1901,6 +1887,7 @@ function BlockLabelFragment(labelString) {
this.labelString = labelString || '';
this.type = '%s'; // null for label, a spec for an input
this.defaultValue = '';
+ this.options = '';
this.isDeleted = false;
}
@@ -1950,6 +1937,7 @@ BlockLabelFragment.prototype.copy = function () {
var ans = new BlockLabelFragment(this.labelString);
ans.type = this.type;
ans.defaultValue = this.defaultValue;
+ ans.options = this.options;
return ans;
};
@@ -2364,6 +2352,7 @@ InputSlotDialogMorph.prototype.init = function (
this.add(this.slots);
this.createSlotTypeButtons();
this.fixSlotsLayout();
+ this.addSlotsMenu();
this.createTypeButtons();
this.fixLayout();
};
@@ -2838,6 +2827,38 @@ InputSlotDialogMorph.prototype.fixSlotsLayout = function () {
this.slots.changed();
};
+InputSlotDialogMorph.prototype.addSlotsMenu = function () {
+ var myself = this;
+
+ this.slots.userMenu = function () {
+ if (contains(['%s', '%n', '%txt', '%anyUE'], myself.fragment.type)) {
+ var menu = new MenuMorph(myself);
+ menu.addItem('options...', 'editSlotOptions');
+ return menu;
+ }
+ return Morph.prototype.userMenu.call(myself);
+ };
+};
+
+InputSlotDialogMorph.prototype.editSlotOptions = function () {
+ var myself = this;
+ new DialogBoxMorph(
+ myself,
+ function (options) {
+ myself.fragment.options = options;
+ },
+ myself
+ ).promptCode(
+ 'Input Slot Options',
+ myself.fragment.options,
+ myself.world(),
+ null,
+ 'Enter one option per line.' +
+ 'Optionally use "=" as key/value delimiter\n' +
+ 'e.g.\n the answer=42'
+ );
+};
+
// InputSlotDialogMorph hiding and showing:
/*
diff --git a/history.txt b/history.txt
index e24e7c3..8bc9b1c 100755
--- a/history.txt
+++ b/history.txt
@@ -1970,7 +1970,7 @@ ______
* Threads: fixed #213 - Empty else block breaks return to caller
131025
-———
+------
* Blocks: enable Costumes as Symbols and Symbols as custom block label parts
* BYOB: Symbol selection menu for BlockLabelFragmentMorphs
* Portuguese translation update
@@ -1978,11 +1978,15 @@ ______
* BYOB: enable Symbols in InputSlotDialog Morph’s drop down menu
131104
-———
+------
* GUI: filter quotation marks from project names (for backend index)
* BYOB: only show symbol menu for label fragments
* BYOB: customizable drop-down menus for input slots (experimental, commented out)
131107
-———
+------
* GUI, Cloud: transmission integrity check
+
+131112
+------
+* Blocks, BYOB, Store: customizable drop-down menus for input slots
diff --git a/store.js b/store.js
index e51c8da..0032919 100644
--- a/store.js
+++ b/store.js
@@ -49,7 +49,7 @@
*/
-/*global modules, XML_Element, XML_Serializer, VariableFrame, StageMorph,
+/*global modules, XML_Element, VariableFrame, StageMorph,
SpriteMorph, WatcherMorph, Point, CustomBlockDefinition, Context,
ReporterBlockMorph, CommandBlockMorph, HatBlockMorph, RingMorph, contains,
detect, CustomCommandBlockMorph, CustomReporterBlockMorph, Color, List,
@@ -61,7 +61,7 @@ SyntaxElementMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.store = '2013-September-17';
+modules.store = '2013-November-12';
// XML_Serializer ///////////////////////////////////////////////////////
@@ -741,12 +741,16 @@ SnapSerializer.prototype.loadCustomBlocks = function (
if (inputs) {
i = -1;
inputs.children.forEach(function (child) {
+ var options = child.childNamed('options');
if (child.tag !== 'input') {
return;
}
i += 1;
- definition.declarations[names[i]]
- = [child.attributes.type, child.contents];
+ definition.declarations[names[i]] = [
+ child.attributes.type,
+ child.contents,
+ options ? options.contents : undefined
+ ];
});
}
@@ -1479,8 +1483,6 @@ VariableFrame.prototype.toXML = function (serializer) {
}, '');
};
-
-
// Watchers
WatcherMorph.prototype.toXML = function (serializer) {
@@ -1661,9 +1663,13 @@ CustomBlockDefinition.prototype.toXML = function (serializer) {
this.codeMapping || '',
Object.keys(this.declarations).reduce(function (xml, decl) {
return xml + serializer.format(
- '<input type="@">$</input>',
+ '<input type="@">$%</input>',
myself.declarations[decl][0],
- myself.declarations[decl][1]
+ myself.declarations[decl][1],
+ myself.declarations[decl][2] ?
+ '<options>' + myself.declarations[decl][2] +
+ '</options>'
+ : ''
);
}, ''),
this.body ? serializer.store(this.body.expression) : '',