new dial widget

* Blocks: added dial widget to POINT IN DIRECTION's drop-down menu
* Objects: added "rotate" option to Sprite context menu
* Threads, Blocks: fixed Joan's fix for #1972, because it broke HOFs
upd4.2
Jens Mönig 2018-01-25 08:34:56 +01:00
rodzic 79c43e7bdc
commit 72cb4c02d4
8 zmienionych plików z 422 dodań i 22 usunięć

Wyświetl plik

@ -144,11 +144,11 @@ fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph,
CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph,
Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil,
isSnapObject, PushButtonMorph, SpriteIconMorph, Process, AlignmentMorph,
CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph*/
CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/
// Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2018-January-22';
modules.blocks = '2018-January-25';
var SyntaxElementMorph;
var BlockMorph;
@ -934,6 +934,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
null,
true,
{
'§_dir': null,
'(90) right' : 90,
'(-90) left' : -90,
'(0) up' : '0',
@ -4067,6 +4068,9 @@ BlockMorph.prototype.situation = function () {
BlockMorph.prototype.prepareToBeGrabbed = function (hand) {
var myself = this;
this.allInputs().forEach(function (input) {
delete input.bindingID;
});
this.allComments().forEach(function (comment) {
comment.startFollowing(myself, hand.world);
});
@ -8257,7 +8261,8 @@ InputSlotMorph.prototype.menuFromDict = function (
noEmptyOption,
enableKeyboard)
{
var key,
var key, dial,
myself = this,
menu = new MenuMorph(
this.userSetContents,
null,
@ -8265,6 +8270,11 @@ InputSlotMorph.prototype.menuFromDict = function (
this.fontSize
);
function update (num) {
myself.setContents(num);
myself.reactToSliderEdit();
}
if (choices instanceof Function) {
choices = choices.call(this);
} else if (isString(choices)) {
@ -8282,6 +8292,17 @@ InputSlotMorph.prototype.menuFromDict = function (
menu.addLine();
} else if (key.indexOf('§_def') === 0) {
menu.addItem(choices[key], choices[key]);
} else if (key.indexOf('§_dir') === 0) {
dial = new DialMorph();
dial.rootForGrab = function () {return this; };
dial.target = this;
dial.action = update;
dial.fillColor = this.parent.color;
dial.setRadius(this.fontSize * 3);
dial.setValue(this.evaluate(), false, true);
menu.addLine();
menu.items.push(dial);
menu.addLine();
} else if (choices[key] instanceof Object &&
!(choices[key] instanceof Array) &&
(typeof choices[key] !== 'function')) {
@ -9199,10 +9220,6 @@ InputSlotMorph.prototype.drawRoundBorder = function (context) {
context.stroke();
};
// TemplateSlotMorph ///////////////////////////////////////////////////
/*

8
gui.js
Wyświetl plik

@ -75,7 +75,7 @@ isRetinaSupported, SliderMorph, Animation*/
// Global stuff ////////////////////////////////////////////////////////
modules.gui = '2018-January-23';
modules.gui = '2018-January-25';
// Declarations
@ -301,7 +301,8 @@ IDE_Morph.prototype.openIn = function (world) {
// prevent non-DialogBoxMorphs from being dropped
// onto the World in user-mode
world.reactToDropOf = function (morph) {
if (!(morph instanceof DialogBoxMorph)) {
if (!(morph instanceof DialogBoxMorph ||
(morph instanceof MenuMorph))) {
if (world.hand.grabOrigin) {
morph.slideBackTo(world.hand.grabOrigin);
} else {
@ -4454,7 +4455,8 @@ IDE_Morph.prototype.switchToUserMode = function () {
// prevent non-DialogBoxMorphs from being dropped
// onto the World in user-mode
world.reactToDropOf = function (morph) {
if (!(morph instanceof DialogBoxMorph)) {
if (!(morph instanceof DialogBoxMorph ||
(morph instanceof MenuMorph))) {
if (world.hand.grabOrigin) {
morph.slideBackTo(world.hand.grabOrigin);
} else {

Wyświetl plik

@ -3868,6 +3868,14 @@ Fixes:
* fixed #1972, thanks, Joan!
* Objects, GUI: When deleting a temporary clone, detach all its parts and delete the temporary ones
180125
------
* Morphic: new DialMorph widget
* Blocks: added dial widget to POINT IN DIRECTION's drop-down menu
* Objects: added "rotate" option to Sprite context menu
* Threads, Blocks: fixed Joan's fix for #1972, because it broke HOFs
v4.1.1 New Features:
* translation support for custom blocks
@ -3875,6 +3883,8 @@ v4.1.1 New Features:
* included local methods in the OF-block's left drop-down menu
* added "width" and "height" selectors to Pixels library
* added scroll events, thanks, Bernat!
* new dial widget POINT IN DIRECTION's drop-down menu
* new "rotate" option for sprite context menu
Notable Changes:
* global and local variables are now separat in the palette, each sorted alphabetically, local vars marked with location pin (only in palette)

Wyświetl plik

@ -185,7 +185,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org', // optional
'last_changed':
'2018-01-22', // this, too, will appear in the Translators tab
'2018-01-25', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -969,6 +969,8 @@ SnapTranslator.dict.de = {
'Angelpunkt',
'edit the costume\'s\nrotation center':
'Drehpunkt des Kostüms\nanzeigen und verschieben',
'rotate':
'Drehen',
'detach from':
'Abtrennen von',
'detach all parts':

Wyświetl plik

@ -42,7 +42,7 @@
/*global modules, contains*/
modules.locale = '2018-January-22';
modules.locale = '2018-January-25';
// Global stuff
@ -160,7 +160,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org',
'last_changed':
'2018-01-22'
'2018-01-25'
};
SnapTranslator.dict.it = {

Wyświetl plik

@ -85,6 +85,7 @@
ColorPaletteMorph
GrayPaletteMorph
ColorPickerMorph
DialMorph
FrameMorph
ScrollFrameMorph
ListMorph
@ -126,6 +127,7 @@
CursorMorph
BoxMorph
SpeechBubbleMorph
DialMorph
CircleBoxMorph
SliderButtonMorph
SliderMorph
@ -1161,7 +1163,7 @@
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
var morphicVersion = '2018-January-22';
var morphicVersion = '2018-January-25';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@ -4843,6 +4845,52 @@ PenMorph.prototype.setHeading = function (degrees) {
this.changed();
};
PenMorph.prototype.numericalSetters = function () {
// for context menu demo purposes
return [
'setLeft',
'setTop',
'setWidth',
'setHeight',
'setAlphaScaled',
'setHeading'
];
};
// PenMorph menu:
PenMorph.prototype.developersMenu = function () {
var menu = PenMorph.uber.developersMenu.call(this);
menu.addLine();
menu.addItem(
'set rotation',
"setRotation",
'interactively turn this morph\nusing a dial widget'
);
return menu;
};
PenMorph.prototype.setRotation = function () {
var menu, dial,
name = this.name || this.constructor.name;
if (name.length > 10) {
name = name.slice(0, 9) + '...';
}
menu = new MenuMorph(this, name);
dial = new DialMorph(null, null, this.heading);
dial.rootForGrab = function () {return this; };
dial.target = this;
dial.action = 'setHeading';
menu.items.push(dial);
menu.addLine();
menu.addItem('(90) right', function () {this.setHeading(90); });
menu.addItem('(-90) left', function () {this.setHeading(-90); });
menu.addItem('(0) up', function () {this.setHeading(0); });
menu.addItem('(180) down', function () {this.setHeading(180); });
menu.isDraggable = true;
menu.popUpAtHand(this.world());
};
// PenMorph drawing:
PenMorph.prototype.drawLine = function (start, dest) {
@ -6245,6 +6293,315 @@ SpeechBubbleMorph.prototype.fixLayout = function () {
this.addShadow(new Point(2, 2), 80);
};
// DialMorph //////////////////////////////////////////////////////
// I am a knob than can be turned to select a number
var DialMorph;
// DialMorph inherits from Morph:
DialMorph.prototype = new Morph();
DialMorph.prototype.constructor = DialMorph;
DialMorph.uber = Morph.prototype;
function DialMorph(min, max, value, tick, radius) {
this.init(min, max, value, tick, radius);
}
DialMorph.prototype.init = function (min, max, value, tick, radius) {
this.target = null;
this.action = null;
this.min = min || 0;
this.max = max || 360;
this.value = Math.max(this.min, (value || 0) % this.max);
this.tick = tick || 15;
this.fillColor = null;
DialMorph.uber.init.call(this);
this.color = new Color(230, 230, 230);
this.noticesTransparentClick = true;
this.setRadius(radius || MorphicPreferences.menuFontSize * 4);
};
DialMorph.prototype.setRadius = function (radius) {
this.radius = radius;
this.setExtent(new Point(this.radius * 2, this.radius * 2));
};
DialMorph.prototype.setValue = function (value, snapToTick, noUpdate) {
var range = this.max - this.min;
value = value || 0;
this.value = this.min + (((+value % range) + range) % range);
if (snapToTick) {
if (this.value < this.tick) {
this.value = this.min;
} else {
this.value -= this.value % this.tick % this.value;
}
}
this.drawNew();
this.changed();
if (noUpdate) {return; }
this.updateTarget();
};
DialMorph.prototype.getValueOf = function (point) {
var range = this.max - this.min,
center = this.center(),
deltaX = point.x - center.x,
deltaY = center.y - point.y,
angle = Math.abs(deltaX) < 0.001 ? (deltaY < 0 ? 90 : 270)
: Math.round(
(deltaX >= 0 ? 0 : 180)
- (Math.atan(deltaY / deltaX) * 57.2957795131)
),
value = angle + 90 % 360,
ratio = value / 360;
return range * ratio + this.min;
};
DialMorph.prototype.setExtent = function (aPoint) {
var size = Math.min(aPoint.x, aPoint.y);
this.radius = size / 2;
DialMorph.uber.setExtent.call(this, new Point(size, size));
};
DialMorph.prototype.drawNew = function () {
var ctx, i, angle, x1, y1, x2, y2,
light = this.color.lighter().toString(),
range = this.max - this.min,
ticks = range / this.tick,
face = this.radius * 0.75,
inner = face * 0.85,
outer = face * 0.95;
this.image = newCanvas(this.extent());
ctx = this.image.getContext('2d');
// draw a light border:
ctx.fillStyle = light;
ctx.beginPath();
ctx.arc(
this.radius,
this.radius,
face + Math.min(1, this.radius - face),
0,
2 * Math.PI,
false
);
ctx.closePath();
ctx.fill();
// fill circle:
ctx.fillStyle = this.color.toString();
ctx.beginPath();
ctx.arc(
this.radius,
this.radius,
face,
0,
2 * Math.PI,
false
);
ctx.closePath();
ctx.fill();
// fill value
angle = (this.value - this.min) * (Math.PI * 2) / range - Math.PI / 2;
ctx.fillStyle = (this.fillColor || this.color.darker()).toString();
ctx.beginPath();
ctx.arc(
this.radius,
this.radius,
face,
Math.PI / -2,
angle,
false
);
ctx.lineTo(this.radius, this.radius);
ctx.closePath();
ctx.fill();
// draw ticks:
ctx.strokeStyle = new Color(35, 35, 35).toString();
ctx.lineWidth = 1;
for (i = 0; i < ticks; i += 1) {
angle = (i - 3) * (Math.PI * 2) / ticks - Math.PI / 2;
ctx.beginPath();
x1 = this.radius + Math.cos(angle) * inner;
y1 = this.radius + Math.sin(angle) * inner;
x2 = this.radius + Math.cos(angle) * outer;
y2 = this.radius + Math.sin(angle) * outer;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
// draw a filled center:
inner = face * 0.05;
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(
this.radius,
this.radius,
inner,
0,
2 * Math.PI,
false
);
ctx.closePath();
ctx.fill();
// draw the inner hand:
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
angle = (this.value - this.min) * (Math.PI * 2) / range - Math.PI / 2;
outer = face * 0.8;
x1 = this.radius + Math.cos(angle) * inner;
y1 = this.radius + Math.sin(angle) * inner;
x2 = this.radius + Math.cos(angle) * outer;
y2 = this.radius + Math.sin(angle) * outer;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
// draw a read-out circle:
inner = inner * 2;
x2 = this.radius + Math.cos(angle) * (outer + inner);
y2 = this.radius + Math.sin(angle) * (outer + inner);
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.arc(
x2,
y2,
inner,
0,
2 * Math.PI,
false
);
ctx.closePath();
ctx.stroke();
// draw the outer hand:
angle = (this.value - this.min) * (Math.PI * 2) / range - Math.PI / 2;
x1 = this.radius + Math.cos(angle) * face;
y1 = this.radius + Math.sin(angle) * face;
x2 = this.radius + Math.cos(angle) * (this.radius - 1);
y2 = this.radius + Math.sin(angle) * (this.radius - 1);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineWidth = 3;
ctx.strokeStyle = light;
ctx.stroke();
ctx.lineWidth = 1;
ctx.strokeStyle = 'black';
ctx.stroke();
// draw arrow tip:
angle = radians(degrees(angle) - 4);
x1 = this.radius + Math.cos(angle) * this.radius * 0.9;
y1 = this.radius + Math.sin(angle) * this.radius * 0.9;
ctx.beginPath();
ctx.moveTo(x1, y1);
angle = radians(degrees(angle) + 8);
x1 = this.radius + Math.cos(angle) * this.radius * 0.9;
y1 = this.radius + Math.sin(angle) * this.radius * 0.9;
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.lineWidth = 3;
ctx.strokeStyle = light;
ctx.stroke();
ctx.lineWidth = 1;
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.fill();
};
// DialMorph stepping:
DialMorph.prototype.step = null;
DialMorph.prototype.mouseDownLeft = function (pos) {
var world, myself = this;
world = this.root();
this.step = function () {
if (world.hand.mouseButton) {
myself.setValue(
myself.getValueOf(world.hand.bounds.origin),
world.currentKey !== 16 // snap to tick
);
} else {
this.step = null;
}
};
};
// DialMorph menu:
DialMorph.prototype.developersMenu = function () {
var menu = DialMorph.uber.developersMenu.call(this);
menu.addLine();
menu.addItem(
'set target',
"setTarget",
'select another morph\nwhose numerical property\nwill be ' +
'controlled by this one'
);
return menu;
};
DialMorph.prototype.setTarget = function () {
var choices = this.overlappedMorphs(),
menu = new MenuMorph(this, 'choose target:'),
myself = this;
choices.push(this.world());
choices.forEach(function (each) {
menu.addItem(each.toString().slice(0, 50), function () {
myself.target = each;
myself.setTargetSetter();
});
});
if (choices.length === 1) {
this.target = choices[0];
this.setTargetSetter();
} else if (choices.length > 0) {
menu.popUpAtHand(this.world());
}
};
DialMorph.prototype.setTargetSetter = function () {
var choices = this.target.numericalSetters(),
menu = new MenuMorph(this, 'choose target property:'),
myself = this;
choices.forEach(function (each) {
menu.addItem(each, function () {
myself.action = each;
});
});
if (choices.length === 1) {
this.action = choices[0];
} else if (choices.length > 0) {
menu.popUpAtHand(this.world());
}
};
DialMorph.prototype.updateTarget = function () {
if (this.action) {
if (typeof this.action === 'function') {
this.action.call(this.target, this.value);
} else { // assume it's a String
this.target[this.action](this.value);
}
}
};
// CircleBoxMorph //////////////////////////////////////////////////////
// I can be used for sliders
@ -7672,7 +8029,8 @@ MenuMorph.prototype.drawNew = function () {
isLine = false;
if (tuple instanceof StringFieldMorph ||
tuple instanceof ColorPickerMorph ||
tuple instanceof SliderMorph) {
tuple instanceof SliderMorph ||
tuple instanceof DialMorph) {
item = tuple;
} else if (tuple[0] === 0) {
isLine = true;
@ -7729,7 +8087,8 @@ MenuMorph.prototype.maxWidth = function () {
);
} else if ((item instanceof StringFieldMorph) ||
(item instanceof ColorPickerMorph) ||
(item instanceof SliderMorph)) {
(item instanceof SliderMorph) ||
(item instanceof DialMorph)) {
w = Math.max(w, item.width());
}
});
@ -7744,7 +8103,9 @@ MenuMorph.prototype.adjustWidths = function () {
isSelected,
myself = this;
this.children.forEach(function (item) {
item.silentSetWidth(w);
if (!(item instanceof DialMorph)) {
item.silentSetWidth(w);
}
if (item instanceof MenuItemMorph) {
item.fixLayout();
isSelected = (item.image === item.pressImage);
@ -10745,7 +11106,9 @@ HandMorph.prototype.grab = function (aMorph) {
if (this.children.length === 0) {
this.world.stopEditing();
this.grabOrigin = aMorph.situation();
aMorph.addShadow();
if (!(aMorph instanceof MenuMorph)) {
aMorph.addShadow();
}
if (aMorph.prepareToBeGrabbed) {
aMorph.prepareToBeGrabbed(this);
}
@ -10770,7 +11133,9 @@ HandMorph.prototype.drop = function () {
morphToDrop.cachedFullImage = null;
morphToDrop.cachedFullBounds = null;
morphToDrop.changed();
morphToDrop.removeShadow();
if (!(morphToDrop instanceof MenuMorph)) {
morphToDrop.removeShadow();
}
this.children = [];
this.setExtent(new Point());
if (morphToDrop.justDropped) {
@ -11895,6 +12260,10 @@ WorldMorph.prototype.userCreateMorph = function () {
menu.addItem('slider', function () {
create(new SliderMorph());
});
menu.addItem('dial', function () {
newMorph = new DialMorph();
newMorph.pickUp(this);
});
menu.addItem('frame', function () {
newMorph = new FrameMorph();
newMorph.setExtent(new Point(350, 250));

Wyświetl plik

@ -83,7 +83,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph, HandleMorph,
AlignmentMorph*/
modules.objects = '2018-January-23';
modules.objects = '2018-January-25';
var SpriteMorph;
var StageMorph;
@ -3227,6 +3227,7 @@ SpriteMorph.prototype.userMenu = function () {
}
menu.addItem("delete", 'remove');
menu.addItem("move", 'moveCenter');
menu.addItem("rotate", 'setRotation');
if (this.costume) {
menu.addItem(
"pivot",

Wyświetl plik

@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph,
TableFrameMorph, ColorSlotMorph, isSnapObject*/
modules.threads = '2018-January-23';
modules.threads = '2018-January-25';
var ThreadManager;
var Process;
@ -855,7 +855,6 @@ Process.prototype.evaluateInput = function (input) {
} else {
ans = this.context.variables.getVar(input.bindingID);
}
delete input.bindingID;
} else {
ans = input.evaluate();
if (ans) {