Skip to content

Instantly share code, notes, and snippets.

@dekrain
Created October 1, 2016 20:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dekrain/5b80ac03263fd36eb3c8370fd6f13e67 to your computer and use it in GitHub Desktop.
Save dekrain/5b80ac03263fd36eb3c8370fd6f13e67 to your computer and use it in GitHub Desktop.
Snap! Block Factory!!!
// A Block Factory for Snap! by (C) ~DK
// Based on morphic.js and Snap!
// Needs: morphic.js, widgets.js, blocks.js, objects.js, gui.js
/*global Morph DialogBoxMorph BlockMorph CommandBlockMorph ReporterBlockMorph AlignmentMorph SpriteMorph ToggleButtonMorph IDE_Morph*/
var BlockFactoryMorph;
// BlockFactoryMorph inherits from DialogBoxMorph
BlockFactoryMorph.prototype = new DialogBoxMorph();
BlockFactoryMorph.prototype.constructor = BlockFactoryMorph;
BlockFactoryMorph.uber = DialogBoxMorph.prototype;
// BlockFactoryMorph opening
BlockFactoryMorph.openIn = function(world) {
var factory = new this();
factory.prompt('Block Factory', factory.spec, world);
return factory;
};
BlockFactoryMorph.openInButton = function(world) {
return new PushButtonMorph(null, function(){BlockFactoryMorph.openIn(world);}, 'Open Dialog');
}
// BlockFactoryMorph instance creation
function BlockFactoryMorph() {
this.init();
}
BlockFactoryMorph.prototype.init = function() {
// Additional properties
this.category = 'motion';
this.shape = 'command';
this.spec = 'Your spec here!';
this.selector = 'A block selector...';
this.block = new CommandBlockMorph();
this.refreshBlock();
// Initialize inherited properties
BlockFactoryMorph.uber.init.call(
this,
this,
'registerBlock',
this
);
// Override inherited properties
this.key = 'buildABlock';
this.shapes = new AlignmentMorph('row', this.padding);
this.add(this.shapes);
this.categories = new BoxMorph();
this.categories.color = SpriteMorph.prototype.paletteColor.lighter(8);
this.categories.borderColor = this.categories.color.lighter(40);
this.add(this.categories);
this.code = SpriteMorph.prototype.blockForSelector('reportJSFunction');
this.add(this.code);
this.selectorInput = new InputFieldMorph(
this.selector,
false
);
this.add(this.selectorInput);
this.createMyButtons();
this.fixMyLayout();
};
// Category buttons
BlockFactoryMorph.prototype.createMyButtons = function() {
this.createCategoryButtons();
this.createShapeButtons();
};
BlockFactoryMorph.prototype.createCategoryButtons = function () {
var myself = this,
oldFlag = Morph.prototype.trackChanges;
Morph.prototype.trackChanges = false;
SpriteMorph.prototype.categories.forEach(function (cat) {
myself.addCategoryButton(cat);
});
Morph.prototype.trackChanges = oldFlag;
};
BlockFactoryMorph.prototype.addCategoryButton = function (category) {
var labelWidth = 75,
myself = this,
colors = [
SpriteMorph.prototype.paletteColor,
SpriteMorph.prototype.paletteColor.darker(50),
SpriteMorph.prototype.blockColor[category]
],
button;
button = new ToggleButtonMorph(
colors,
this, // this block dialog box is the target
function () {
myself.category = category;
myself.categories.children.forEach(function (each) {
each.refresh();
});
if (myself.shapes) {
myself.shapes.children.forEach(function (each) {
each.setColor(colors[2]);
});
}
myself.edit();
},
category[0].toUpperCase().concat(category.slice(1)), // UCase label
function () {return myself.category === category; }, // query
null, // env
null, // hint
null, // template cache
labelWidth, // minWidth
true // has preview
);
button.corner = 8;
button.padding = 0;
button.labelShadowOffset = new Point(-1, -1);
button.labelShadowColor = colors[1];
button.labelColor = IDE_Morph.prototype.buttonLabelColor;
button.contrast = this.buttonContrast;
button.fixLayout();
button.refresh();
this.categories.add(button);
return button;
};
BlockFactoryMorph.prototype.createShapeButtons = function () {
var block,
myself = this,
clr = SpriteMorph.prototype.blockColor[this.category];
block = new CommandBlockMorph();
block.setColor(clr);
block.setSpec(localize('Command'));
this.addBlockShapeButton(
function () {myself.setShape('command'); },
block,
function () {return myself.shape === 'command'; }
);
block = new ReporterBlockMorph();
block.setColor(clr);
block.setSpec(localize('Reporter'));
this.addBlockShapeButton(
function () {myself.setShape('reporter'); },
block,
function () {return myself.shape === 'reporter'; }
);
block = new ReporterBlockMorph(true);
block.setColor(clr);
block.setSpec(localize('Predicate'));
this.addBlockShapeButton(
function () {myself.setShape('predicate'); },
block,
function () {return myself.shape === 'predicate'; }
);
};
BlockFactoryMorph.prototype.addBlockShapeButton = function (
action,
element,
query
) {
var button = new ToggleElementMorph(
this,
action,
element,
query,
null,
null,
'rebuild'
);
button.refresh();
this.shapes.add(button);
return button;
};
BlockFactoryMorph.prototype.refreshBlock = function() {
var shapes = ['command', ['reporter', 'predicate']];
var shape = shapes[[CommandBlockMorph, ReporterBlockMorph].indexOf(this.block.constructor)];
if (shape instanceof Array) shape = shape[this.block.isPredicate ? 1 : 0];
if (shape !== this.shape) this.block = (function(){
switch (this.shape) {
case 'command': return new CommandBlockMorph();
case 'reporter': return new ReporterBlockMorph();
case 'predicate': return new ReporterBlockMorph(true);
default: throw new TypeError('Invalid block shape!');
}
}.call(this));
this.block.blockSpec = this.spec;
this.block.category = this.category;
this.block.selector = this.selector;
this.block.rebuild();
this.block.fixBlockColor();
this.block.drawNew();
};
// BlockFactoryMorph layout
BlockFactoryMorph.prototype.fixLayout = function() {
var th = fontHeight(this.titleFontSize) + this.titlePadding * 2, w, n = this.top() + th;
if (this.head) {
this.head.setPosition(this.position().add(new Point(
this.padding,
th + this.padding
)));
this.silentSetWidth(this.head.width() + this.padding * 2);
this.silentSetHeight(
this.head.height()
+ this.padding * 2
+ th
);
}
if (this.body) {
if (this.head) {
this.body.setPosition(this.head.bottomLeft().add(new Point(
0,
this.padding
)));
this.silentSetWidth(Math.max(
this.width(),
this.body.width() + this.padding * 2
));
this.silentSetHeight(
this.height()
+ this.body.height()
+ this.padding
);
w = this.width();
this.head.setLeft(
this.left()
+ Math.round((w - this.head.width()) / 2)
);
this.body.setLeft(
this.left()
+ Math.round((w - this.body.width()) / 2)
);
} else {
this.body.setPosition(this.position().add(new Point(
this.padding,
th + this.padding
)));
this.silentSetWidth(this.body.width() + this.padding * 2);
this.silentSetHeight(
this.body.height()
+ this.padding * 2
+ th
);
}
}
n += 10;
if (this.categories) {
this.categories.setCenter(this.center());
this.categories.setTop(n);
n = this.categories.bottom() + 15;
}
if (this.shapes) {
this.shapes.fixLayout();
this.shapes.setCenter(this.center());
this.shapes.setTop(n);
n = this.shapes.bottom() + 20;
}
if (this.label) {
this.label.setCenter(this.center());
this.label.setTop(this.top() + (th - this.label.height()) / 2);
}
if (this.code) {
this.code.setLeft(this.left() + 15);
this.code.setTop(n);
this.code.labelWidth = this.width() - (2 * 15);
n = this.code.bottom() + 10;
}
if (this.body) {
this.body.setTop(n+15);
n = this.body.bottom() + 5;
this.selectorInput.setWidth(this.body.width());
this.selectorInput.setTop(n);
this.selectorInput.setLeft(this.body.left());
n = this.selectorInput.bottom() + 15;
}
this.silentSetHeight(n-this.top());
if (this.buttons && (this.buttons.children.length > 0)) {
this.buttons.fixLayout();
this.silentSetHeight(
this.height()
+ this.buttons.height()
+ this.padding
);
this.silentSetWidth(Math.max(
this.width(),
this.buttons.width()
+ (2 * this.padding)
)
);
this.buttons.setCenter(this.center());
this.buttons.setBottom(this.bottom() - this.padding);
}
};
BlockFactoryMorph.prototype.fixMyLayout = function() {
this.fixCategoriesLayout();
};
BlockFactoryMorph.prototype.fixCategoriesLayout = function () {
var buttonWidth = this.categories.children[0].width(), // all the same
buttonHeight = this.categories.children[0].height(), // all the same
xPadding = 15,
yPadding = 2,
border = 10, // this.categories.border,
rows = Math.ceil((this.categories.children.length) / 2),
l = this.categories.left(),
t = this.categories.top(),
i = 0,
row,
col,
oldFlag = Morph.prototype.trackChanges;
Morph.prototype.trackChanges = false;
this.categories.children.forEach(function (button) {
i += 1;
row = Math.ceil(i / 2);
col = 2 - (i % 2);
button.setPosition(new Point(
l + (col * xPadding + ((col - 1) * buttonWidth)),
t + (row * yPadding + ((row - 1) * buttonHeight) + border)
));
});
if (MorphicPreferences.isFlat) {
this.categories.corner = 0;
this.categories.border = 0;
this.categories.edge = 0;
}
this.categories.setExtent(new Point(
3 * xPadding + 2 * buttonWidth,
(rows + 1) * yPadding + rows * buttonHeight + 2 * border
));
Morph.prototype.trackChanges = oldFlag;
this.categories.changed();
};
BlockFactoryMorph.prototype.setShape = function(shape) {
if (['command', 'reporter', 'predicate'].indexOf(shape) > -1) {
this.shape = shape;
this.shapes.children.forEach(function(c){
c.refresh();
});
this.edit();
}
}
BlockFactoryMorph.prototype.registerBlock = function(spec) {
this.spec = spec;
this.selector = this.selectorInput.getValue();
console.log(invoke(this.code));
console.log(this.code.inputs()[0].evaluate().concat([this.code.inputs()[1].evaluate()]));
this.refreshBlock();
this.block.isDraggable = true;
this.block.pickUp(this.world());
this.block.fullChanged();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment