customizable drop-down menus for input slots

pull/3/merge
jmoenig 2013-11-12 11:44:39 +01:00
rodzic cc7a1558f1
commit 651f44ebdf
4 zmienionych plików z 106 dodań i 61 usunięć

Wyświetl plik

@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2013-October-25'; modules.blocks = '2013-November-12';
var SyntaxElementMorph; var SyntaxElementMorph;
var BlockMorph; var BlockMorph;
@ -517,6 +517,12 @@ SyntaxElementMorph.prototype.revertToDefaultInput = function (arg, noValues) {
if (idx !== -1) { if (idx !== -1) {
if (this instanceof BlockMorph) { if (this instanceof BlockMorph) {
deflt = this.labelPart(this.parseSpec(this.blockSpec)[idx]); 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) { } else if (this instanceof MultiArgMorph) {
deflt = this.labelPart(this.slotSpec); deflt = this.labelPart(this.slotSpec);
} else if (this instanceof ReporterSlotMorph) { } else if (this instanceof ReporterSlotMorph) {
@ -1889,7 +1895,8 @@ BlockMorph.prototype.parseSpec = function (spec) {
BlockMorph.prototype.setSpec = function (spec) { BlockMorph.prototype.setSpec = function (spec) {
var myself = this, var myself = this,
part; part,
inputIdx = -1;
if (!spec) {return; } if (!spec) {return; }
this.parts().forEach(function (part) { this.parts().forEach(function (part) {
@ -1899,6 +1906,9 @@ BlockMorph.prototype.setSpec = function (spec) {
this.add(this.placeHolder()); this.add(this.placeHolder());
} }
this.parseSpec(spec).forEach(function (word) { this.parseSpec(spec).forEach(function (word) {
if (word[0] === '%') {
inputIdx += 1;
}
part = myself.labelPart(word); part = myself.labelPart(word);
myself.add(part); myself.add(part);
if (!(part instanceof CommandSlotMorph)) { if (!(part instanceof CommandSlotMorph)) {
@ -1917,6 +1927,10 @@ BlockMorph.prototype.setSpec = function (spec) {
if (myself.isPrototype) { if (myself.isPrototype) {
myself.add(myself.placeHolder()); 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.blockSpec = spec;
this.fixLayout(); this.fixLayout();
@ -2325,7 +2339,7 @@ BlockMorph.prototype.mapToHeader = function () {
} else { } else {
help = 'Enter code that corresponds to the block\'s definition. ' + help = 'Enter code that corresponds to the block\'s definition. ' +
'Choose your own\nformal parameter names (ignoring the ones ' + 'Choose your own\nformal parameter names (ignoring the ones ' +
'shown .'; 'shown).';
} }
new DialogBoxMorph( new DialogBoxMorph(
this, this,

115
byob.js
Wyświetl plik

@ -102,11 +102,11 @@ ArrowMorph, PushButtonMorph, contains, InputSlotMorph, ShadowMorph,
ToggleButtonMorph, IDE_Morph, MenuMorph, copy, ToggleElementMorph, ToggleButtonMorph, IDE_Morph, MenuMorph, copy, ToggleElementMorph,
Morph, fontHeight, StageMorph, SyntaxElementMorph, SnapSerializer, Morph, fontHeight, StageMorph, SyntaxElementMorph, SnapSerializer,
CommentMorph, localize, CSlotMorph, SpeechBubbleMorph, MorphicPreferences, CommentMorph, localize, CSlotMorph, SpeechBubbleMorph, MorphicPreferences,
SymbolMorph*/ SymbolMorph, isNil*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.byob = '2013-November-04'; modules.byob = '2013-November-12';
// Declarations // Declarations
@ -137,7 +137,7 @@ function CustomBlockDefinition(spec, receiver) {
this.isGlobal = false; this.isGlobal = false;
this.type = 'command'; this.type = 'command';
this.spec = spec || ''; this.spec = spec || '';
this.declarations = {}; // {'inputName' : [type, default]} this.declarations = {}; // {'inputName' : [type, default, options]}
this.comment = null; this.comment = null;
this.codeMapping = null; // experimental, generate text code this.codeMapping = null; // experimental, generate text code
this.codeHeader = null; // experimental, generate text code this.codeHeader = null; // experimental, generate text code
@ -192,6 +192,7 @@ CustomBlockDefinition.prototype.prototypeInstance = function () {
if (slot) { if (slot) {
part.fragment.type = slot[0]; part.fragment.type = slot[0];
part.fragment.defaultValue = slot[1]; part.fragment.defaultValue = slot[1];
part.fragment.options = slot[2];
} }
} }
}); });
@ -265,6 +266,23 @@ CustomBlockDefinition.prototype.defaultValueOfInputIdx = function (idx) {
return this.defaultValueOf(inputName); 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 () { CustomBlockDefinition.prototype.inputNames = function () {
var vNames = [], var vNames = [],
parts = this.parseSpec(this.spec); parts = this.parseSpec(this.spec);
@ -334,6 +352,13 @@ CustomCommandBlockMorph.prototype.refresh = function () {
this.fixBlockColor(); this.fixBlockColor();
this.fixLabelColor(); this.fixLabelColor();
this.restoreInputs(oldInputs); 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 // 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 () { CustomCommandBlockMorph.prototype.refreshPrototype = function () {
// create my label parts from my (edited) fragments only // create my label parts from my (edited) fragments only
var hat, var hat,
@ -579,8 +562,11 @@ CustomCommandBlockMorph.prototype.declarationsFromFragments = function () {
this.parts().forEach(function (part) { this.parts().forEach(function (part) {
if (part instanceof BlockInputFragmentMorph) { if (part instanceof BlockInputFragmentMorph) {
ans[part.fragment.labelString] = ans[part.fragment.labelString] = [
[part.fragment.type, part.fragment.defaultValue]; part.fragment.type,
part.fragment.defaultValue,
part.fragment.options
];
} }
}); });
return ans; return ans;
@ -1901,6 +1887,7 @@ function BlockLabelFragment(labelString) {
this.labelString = labelString || ''; this.labelString = labelString || '';
this.type = '%s'; // null for label, a spec for an input this.type = '%s'; // null for label, a spec for an input
this.defaultValue = ''; this.defaultValue = '';
this.options = '';
this.isDeleted = false; this.isDeleted = false;
} }
@ -1950,6 +1937,7 @@ BlockLabelFragment.prototype.copy = function () {
var ans = new BlockLabelFragment(this.labelString); var ans = new BlockLabelFragment(this.labelString);
ans.type = this.type; ans.type = this.type;
ans.defaultValue = this.defaultValue; ans.defaultValue = this.defaultValue;
ans.options = this.options;
return ans; return ans;
}; };
@ -2364,6 +2352,7 @@ InputSlotDialogMorph.prototype.init = function (
this.add(this.slots); this.add(this.slots);
this.createSlotTypeButtons(); this.createSlotTypeButtons();
this.fixSlotsLayout(); this.fixSlotsLayout();
this.addSlotsMenu();
this.createTypeButtons(); this.createTypeButtons();
this.fixLayout(); this.fixLayout();
}; };
@ -2838,6 +2827,38 @@ InputSlotDialogMorph.prototype.fixSlotsLayout = function () {
this.slots.changed(); 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: // InputSlotDialogMorph hiding and showing:
/* /*

Wyświetl plik

@ -1970,7 +1970,7 @@ ______
* Threads: fixed #213 - Empty else block breaks return to caller * Threads: fixed #213 - Empty else block breaks return to caller
131025 131025
——— ------
* Blocks: enable Costumes as Symbols and Symbols as custom block label parts * Blocks: enable Costumes as Symbols and Symbols as custom block label parts
* BYOB: Symbol selection menu for BlockLabelFragmentMorphs * BYOB: Symbol selection menu for BlockLabelFragmentMorphs
* Portuguese translation update * Portuguese translation update
@ -1978,11 +1978,15 @@ ______
* BYOB: enable Symbols in InputSlotDialog Morphs drop down menu * BYOB: enable Symbols in InputSlotDialog Morphs drop down menu
131104 131104
——— ------
* GUI: filter quotation marks from project names (for backend index) * GUI: filter quotation marks from project names (for backend index)
* BYOB: only show symbol menu for label fragments * BYOB: only show symbol menu for label fragments
* BYOB: customizable drop-down menus for input slots (experimental, commented out) * BYOB: customizable drop-down menus for input slots (experimental, commented out)
131107 131107
——— ------
* GUI, Cloud: transmission integrity check * GUI, Cloud: transmission integrity check
131112
------
* Blocks, BYOB, Store: customizable drop-down menus for input slots

Wyświetl plik

@ -49,7 +49,7 @@
*/ */
/*global modules, XML_Element, XML_Serializer, VariableFrame, StageMorph, /*global modules, XML_Element, VariableFrame, StageMorph,
SpriteMorph, WatcherMorph, Point, CustomBlockDefinition, Context, SpriteMorph, WatcherMorph, Point, CustomBlockDefinition, Context,
ReporterBlockMorph, CommandBlockMorph, HatBlockMorph, RingMorph, contains, ReporterBlockMorph, CommandBlockMorph, HatBlockMorph, RingMorph, contains,
detect, CustomCommandBlockMorph, CustomReporterBlockMorph, Color, List, detect, CustomCommandBlockMorph, CustomReporterBlockMorph, Color, List,
@ -61,7 +61,7 @@ SyntaxElementMorph*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.store = '2013-September-17'; modules.store = '2013-November-12';
// XML_Serializer /////////////////////////////////////////////////////// // XML_Serializer ///////////////////////////////////////////////////////
@ -741,12 +741,16 @@ SnapSerializer.prototype.loadCustomBlocks = function (
if (inputs) { if (inputs) {
i = -1; i = -1;
inputs.children.forEach(function (child) { inputs.children.forEach(function (child) {
var options = child.childNamed('options');
if (child.tag !== 'input') { if (child.tag !== 'input') {
return; return;
} }
i += 1; i += 1;
definition.declarations[names[i]] definition.declarations[names[i]] = [
= [child.attributes.type, child.contents]; child.attributes.type,
child.contents,
options ? options.contents : undefined
];
}); });
} }
@ -1479,8 +1483,6 @@ VariableFrame.prototype.toXML = function (serializer) {
}, ''); }, '');
}; };
// Watchers // Watchers
WatcherMorph.prototype.toXML = function (serializer) { WatcherMorph.prototype.toXML = function (serializer) {
@ -1661,9 +1663,13 @@ CustomBlockDefinition.prototype.toXML = function (serializer) {
this.codeMapping || '', this.codeMapping || '',
Object.keys(this.declarations).reduce(function (xml, decl) { Object.keys(this.declarations).reduce(function (xml, decl) {
return xml + serializer.format( return xml + serializer.format(
'<input type="@">$</input>', '<input type="@">$%</input>',
myself.declarations[decl][0], 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) : '', this.body ? serializer.store(this.body.expression) : '',