pull/29/head
bromagosa 2017-01-04 10:30:48 +01:00
commit 4ce250a2ef
13 zmienionych plików z 302 dodań i 137 usunięć

Wyświetl plik

@ -1,12 +1,12 @@
atom-playground.jpg Atom Playground atom_playground.jpg Atom Playground
bedroom1.gif Bedroom 1 bedroom1.gif Bedroom 1
bedroom2.gif Bedroom 2 bedroom2.gif Bedroom 2
berkeley-mural.jpg Berkeley Mural berkeley_mural.jpg Berkeley Mural
brick-wall-and-stairs.jpg Brick Wall and Stairs brick-wall-and-stairs.jpg Brick Wall and Stairs
brick-wall1.jpg Brick Wall 1 brick-wall1.jpg Brick Wall 1
brick-wall2.jpg Brick Wall 2 brick-wall2.jpg Brick Wall 2
desert.gif Desert desert.gif Desert
night-city-with-street.gif Night City with Street night_city_with_street.gif Night City with Street
party-room.jpg Party Room party_room.jpg Party Room
pathway.jpg Pathway pathway.jpg Pathway
xy-grid.gif XY Grid xy-grid.gif XY Grid

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 75 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 96 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 108 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 20 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 31 KiB

Wyświetl plik

@ -9,7 +9,7 @@
written by Jens Mönig written by Jens Mönig
jens@moenig.org jens@moenig.org
Copyright (C) 2016 by Jens Mönig Copyright (C) 2017 by Jens Mönig
This file is part of Snap!. This file is part of Snap!.
@ -149,7 +149,7 @@ isSnapObject, copy, PushButtonMorph, SpriteIconMorph, Process, AlignmentMorph*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2016-December-27'; modules.blocks = '2017-January-03';
var SyntaxElementMorph; var SyntaxElementMorph;
var BlockMorph; var BlockMorph;
@ -5914,7 +5914,7 @@ ScriptsMorph.prototype.userMenu = function () {
if (this.dropRecord) { if (this.dropRecord) {
if (this.dropRecord.lastRecord) { if (this.dropRecord.lastRecord) {
hasUndropQueue = true; hasUndropQueue = true;
menu.addItem( menu.addPair(
[ [
new SymbolMorph( new SymbolMorph(
'turnBack', 'turnBack',
@ -5923,12 +5923,13 @@ ScriptsMorph.prototype.userMenu = function () {
localize('undrop') localize('undrop')
], ],
'undrop', 'undrop',
'⌘Z',
'undo the last\nblock drop\nin this pane' 'undo the last\nblock drop\nin this pane'
); );
} }
if (this.dropRecord.nextRecord) { if (this.dropRecord.nextRecord) {
hasUndropQueue = true; hasUndropQueue = true;
menu.addItem( menu.addPair(
[ [
new SymbolMorph( new SymbolMorph(
'turnForward', 'turnForward',
@ -5937,6 +5938,7 @@ ScriptsMorph.prototype.userMenu = function () {
localize('redrop') localize('redrop')
], ],
'redrop', 'redrop',
'⌘Y',
'redo the last undone\nblock drop\nin this pane' 'redo the last undone\nblock drop\nin this pane'
); );
} }
@ -7722,8 +7724,19 @@ InputSlotMorph.prototype.setContents = function (aStringOrFloat) {
// InputSlotMorph drop-down menu: // InputSlotMorph drop-down menu:
InputSlotMorph.prototype.dropDownMenu = function (enableKeyboard) { InputSlotMorph.prototype.dropDownMenu = function (enableKeyboard) {
var choices = this.choices, var menu = this.menuFromDict(this.choices);
key, if (menu.items.length > 0) {
if (enableKeyboard) {
menu.popup(this.world(), this.bottomLeft());
menu.getFocus();
} else {
menu.popUpAtHand(this.world());
}
}
};
InputSlotMorph.prototype.menuFromDict = function (choices, noEmptyOption) {
var key,
menu = new MenuMorph( menu = new MenuMorph(
this.setContents, this.setContents,
null, null,
@ -7736,31 +7749,24 @@ InputSlotMorph.prototype.dropDownMenu = function (enableKeyboard) {
} else if (isString(choices)) { } else if (isString(choices)) {
choices = this[choices](); choices = this[choices]();
} }
if (!choices) { if (!noEmptyOption) {
return null; menu.addItem(' ', null);
} }
menu.addItem(' ', null);
for (key in choices) { for (key in choices) {
if (Object.prototype.hasOwnProperty.call(choices, key)) { if (Object.prototype.hasOwnProperty.call(choices, key)) {
if (key[0] === '~') { if (key[0] === '~') {
menu.addLine(); menu.addLine();
// } else if (key.indexOf('§_def') === 0) { // } else if (key.indexOf('§_def') === 0) {
// menu.addItem(choices[key].blockInstance(), choices[key]); // menu.addItem(choices[key].blockInstance(), choices[key]);
} else if (choices[key] instanceof Object &&
!(choices[key] instanceof Array)) {
menu.addMenu(key, this.menuFromDict(choices[key], true));
} else { } else {
menu.addItem(key, choices[key]); menu.addItem(key, choices[key]);
} }
} }
} }
if (menu.items.length > 0) { return menu;
if (enableKeyboard) {
menu.popup(this.world(), this.bottomLeft());
menu.getFocus();
} else {
menu.popUpAtHand(this.world());
}
} else {
return null;
}
}; };
InputSlotMorph.prototype.messagesMenu = function () { InputSlotMorph.prototype.messagesMenu = function () {
@ -7793,7 +7799,6 @@ InputSlotMorph.prototype.messagesMenu = function () {
myself.world() myself.world()
); );
}; };
return dict; return dict;
}; };
@ -7825,7 +7830,6 @@ InputSlotMorph.prototype.messagesReceivedMenu = function () {
myself.world() myself.world()
); );
}; };
return dict; return dict;
}; };

76
byob.js
Wyświetl plik

@ -9,7 +9,7 @@
written by Jens Mönig written by Jens Mönig
jens@moenig.org jens@moenig.org
Copyright (C) 2016 by Jens Mönig Copyright (C) 2017 by Jens Mönig
This file is part of Snap!. This file is part of Snap!.
@ -107,7 +107,7 @@ SymbolMorph, isNil, CursorMorph, VariableFrame, WatcherMorph, Variable*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.byob = '2016-December-29'; modules.byob = '2017-January-03';
// Declarations // Declarations
@ -291,17 +291,31 @@ CustomBlockDefinition.prototype.inputOptionsOfIdx = function (idx) {
}; };
CustomBlockDefinition.prototype.dropDownMenuOf = function (inputName) { CustomBlockDefinition.prototype.dropDownMenuOf = function (inputName) {
var dict = {};
if (this.declarations[inputName] && this.declarations[inputName][2]) { if (this.declarations[inputName] && this.declarations[inputName][2]) {
this.declarations[inputName][2].split('\n').forEach(function (line) { return this.parseChoices(this.declarations[inputName][2]);
var pair = line.split('=');
dict[pair[0]] = isNil(pair[1]) ? pair[0] : pair[1];
});
return dict;
} }
return null; return null;
}; };
CustomBlockDefinition.prototype.parseChoices = function (string) {
var dict = {},
stack = [dict];
string.split('\n').forEach(function (line) {
var pair = line.split('=');
if (pair[0] === '}') {
stack.pop();
dict = stack[stack.length - 1];
} else if (pair[1] === '{') {
dict = {};
stack[stack.length - 1][pair[0]] = dict;
stack.push(dict);
} else {
dict[pair[0]] = isNil(pair[1]) ? pair[0] : pair[1];
}
});
return dict;
};
CustomBlockDefinition.prototype.isReadOnlyInput = function (inputName) { CustomBlockDefinition.prototype.isReadOnlyInput = function (inputName) {
return this.declarations[inputName] && return this.declarations[inputName] &&
this.declarations[inputName][3] === true; this.declarations[inputName][3] === true;
@ -945,47 +959,6 @@ CustomCommandBlockMorph.prototype.deleteBlockDefinition = function () {
); );
}; };
// CustomCommandBlockMorph events:
// hover help - commented out for now
/*
CustomCommandBlockMorph.prototype.mouseEnter = function () {
var comment, help;
if (this.isTemplate && this.definition.comment) {
comment = this.definition.comment.fullCopy();
comment.contents.parse();
help = '';
comment.contents.lines.forEach(function (line) {
help = help + '\n' + line;
});
this.popUpbubbleHelp(
help.substr(1),
this.definition.comment.color
);
}
};
CustomCommandBlockMorph.prototype.mouseLeave = function () {
if (this.isTemplate && this.definition.comment) {
this.world().hand.destroyTemporaries();
}
};
CustomCommandBlockMorph.prototype.popUpbubbleHelp = function (
contents,
color
) {
new SpeechBubbleMorph(
contents,
color,
null,
1
).popUp(this.world(), this.rightCenter().add(new Point(-8, 0)));
};
*/
// CustomCommandBlockMorph relabelling // CustomCommandBlockMorph relabelling
CustomCommandBlockMorph.prototype.relabel = function (alternatives) { CustomCommandBlockMorph.prototype.relabel = function (alternatives) {
@ -3254,8 +3227,9 @@ InputSlotDialogMorph.prototype.editSlotOptions = function () {
myself.fragment.options, myself.fragment.options,
myself.world(), myself.world(),
null, null,
localize('Enter one option per line.' + localize('Enter one option per line.\n' +
'Optionally use "=" as key/value delimiter\n' + 'Optionally use "=" as key/value delimiter ' +
'and {} for submenus. ' +
'e.g.\n the answer=42') 'e.g.\n the answer=42')
); );
}; };

12
gui.js
Wyświetl plik

@ -9,7 +9,7 @@
written by Jens Mönig written by Jens Mönig
jens@moenig.org jens@moenig.org
Copyright (C) 2016 by Jens Mönig Copyright (C) 2017 by Jens Mönig
This file is part of Snap!. This file is part of Snap!.
@ -72,7 +72,7 @@ isRetinaSupported, SliderMorph, Animation*/
// Global stuff //////////////////////////////////////////////////////// // Global stuff ////////////////////////////////////////////////////////
modules.gui = '2016-December-29'; modules.gui = '2017-January-03';
// Declarations // Declarations
@ -2798,9 +2798,9 @@ IDE_Morph.prototype.projectMenu = function () {
menu = new MenuMorph(this); menu = new MenuMorph(this);
menu.addItem('Project notes...', 'editProjectNotes'); menu.addItem('Project notes...', 'editProjectNotes');
menu.addLine(); menu.addLine();
menu.addItem('New', 'createNewProject'); menu.addPair('New', 'createNewProject', '⌘N');
menu.addItem('Open...', 'openProjectsBrowser'); menu.addPair('Open...', 'openProjectsBrowser', '⌘O');
menu.addItem('Save', "save"); menu.addPair('Save', "save", '⌘S');
menu.addItem('Save As...', 'saveProjectsBrowser'); menu.addItem('Save As...', 'saveProjectsBrowser');
menu.addLine(); menu.addLine();
menu.addItem( menu.addItem(
@ -3209,7 +3209,7 @@ IDE_Morph.prototype.aboutSnap = function () {
world = this.world(); world = this.world();
aboutTxt = 'Snap! 4.0.10 - dev -\nBuild Your Own Blocks\n\n' aboutTxt = 'Snap! 4.0.10 - dev -\nBuild Your Own Blocks\n\n'
+ 'Copyright \u24B8 2016 Jens M\u00F6nig and ' + 'Copyright \u24B8 2017 Jens M\u00F6nig and '
+ 'Brian Harvey\n' + 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n' + 'jens@moenig.org, bh@cs.berkeley.edu\n\n'

Wyświetl plik

@ -3228,6 +3228,20 @@ http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation
* BYOB: Disabled hover-help for custom blocks (some people find it annoying) * BYOB: Disabled hover-help for custom blocks (some people find it annoying)
* GUI: Hide setting for “prefer smooth animations” (now - mostly - redundant) * GUI: Hide setting for “prefer smooth animations” (now - mostly - redundant)
161231
------
* Morphic: support for menu shortcuts (ongoing)
* GUI, Blocks: menu shortcuts (experimental)
170102
------
* Morphic: use animations to schedule tool tips
170103
------
* Hierarchical menus, thanks, Brian!
* Tweaks to hierarchical menus
== v4.10 === (in development) == v4.10 === (in development)
@ -3242,6 +3256,7 @@ Features:
* svg support for images from the web (svg files have been supported for a long time) * svg support for images from the web (svg files have been supported for a long time)
* use media dialog for browsing and importing sounds * use media dialog for browsing and importing sounds
* highly experimental infix-expression-to-reporter parser. Thanks, Bernat, for the brilliant idea to add it to the search-blocks field! * highly experimental infix-expression-to-reporter parser. Thanks, Bernat, for the brilliant idea to add it to the search-blocks field!
* hierarchical menus, also for custom blocks, thanks, Brian!
Fixes: Fixes:
* Music (play note) to work again in new and recent browser versions (Chrome 55) * Music (play note) to work again in new and recent browser versions (Chrome 55)

Wyświetl plik

@ -8,7 +8,7 @@
written by Jens Mönig written by Jens Mönig
jens@moenig.org jens@moenig.org
Copyright (C) 2016 by Jens Mönig Copyright (C) 2017 by Jens Mönig
This file is part of Snap!. This file is part of Snap!.
@ -1136,7 +1136,7 @@
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/ /*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
var morphicVersion = '2016-December-27'; var morphicVersion = '2017-January-03';
var modules = {}; // keep track of additional loaded modules var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@ -3347,7 +3347,8 @@ Morph.prototype.toggleVisibility = function () {
// Morph full image: // Morph full image:
Morph.prototype.fullImageClassic = function () { Morph.prototype.fullImageClassic = function () {
var fb = this.cachedFullBounds || this.fullBounds(), // use the cache since fullDrawOn() will // use the cache since fullDrawOn() will
var fb = this.cachedFullBounds || this.fullBounds(),
img = newCanvas(fb.extent()), img = newCanvas(fb.extent()),
ctx = img.getContext('2d'); ctx = img.getContext('2d');
ctx.translate(-fb.origin.x, -fb.origin.y); ctx.translate(-fb.origin.x, -fb.origin.y);
@ -4051,9 +4052,15 @@ Morph.prototype.hierarchyMenu = function () {
parents.forEach(function (each) { parents.forEach(function (each) {
if (each.developersMenu && (each !== world)) { if (each.developersMenu && (each !== world)) {
menu.addMenu(
each.toString().slice(0, 50),
each.developersMenu()
);
/*
menu.addItem(each.toString().slice(0, 50), function () { menu.addItem(each.toString().slice(0, 50), function () {
each.developersMenu().popUpAtHand(world); each.developersMenu().popUpAtHand(world);
}); });
*/
} }
}); });
return menu; return menu;
@ -4067,12 +4074,7 @@ Morph.prototype.developersMenu = function () {
menu = new MenuMorph(this, this.constructor.name || menu = new MenuMorph(this, this.constructor.name ||
this.constructor.toString().split(' ')[1].split('(')[0]); this.constructor.toString().split(' ')[1].split('(')[0]);
if (userMenu) { if (userMenu) {
menu.addItem( menu.addMenu('user features', userMenu);
'user features...',
function () {
userMenu.popUpAtHand(world);
}
);
menu.addLine(); menu.addLine();
} }
menu.addItem( menu.addItem(
@ -7470,6 +7472,7 @@ MenuMorph.prototype.init = function (target, title, environment, fontSize) {
this.isListContents = false; this.isListContents = false;
this.hasFocus = false; this.hasFocus = false;
this.selection = null; this.selection = null;
this.submenu = null;
// initialize inherited properties: // initialize inherited properties:
MenuMorph.uber.init.call(this); MenuMorph.uber.init.call(this);
@ -7489,7 +7492,8 @@ MenuMorph.prototype.addItem = function (
color, color,
bold, // bool bold, // bool
italic, // bool italic, // bool
doubleClickAction // optional, when used as list contents doubleClickAction, // optional, when used as list contents
shortcut // optional string, icon (Morph or Canvas) or tuple [icon, string]
) { ) {
/* /*
labelString is normally a single-line string. But it can also be one labelString is normally a single-line string. But it can also be one
@ -7506,7 +7510,16 @@ MenuMorph.prototype.addItem = function (
color, color,
bold || false, bold || false,
italic || false, italic || false,
doubleClickAction]); doubleClickAction,
shortcut]);
};
MenuMorph.prototype.addMenu = function (label, aMenu, indicator) {
this.addPair(label, aMenu, isNil(indicator) ? '\u25ba' : indicator);
};
MenuMorph.prototype.addPair = function (label, action, shortcut, hint) {
this.addItem(label, action, hint, null, null, null, null, shortcut);
}; };
MenuMorph.prototype.addLine = function (width) { MenuMorph.prototype.addLine = function (width) {
@ -7598,7 +7611,8 @@ MenuMorph.prototype.drawNew = function () {
tuple[3], // color tuple[3], // color
tuple[4], // bold tuple[4], // bold
tuple[5], // italic tuple[5], // italic
tuple[6] // doubleclick action tuple[6], // doubleclick action
tuple[7] // shortcut
); );
} }
if (isLine) { if (isLine) {
@ -7627,9 +7641,12 @@ MenuMorph.prototype.maxWidth = function () {
} }
} }
this.children.forEach(function (item) { this.children.forEach(function (item) {
if (item instanceof MenuItemMorph) { if (item instanceof MenuItemMorph) {
w = Math.max(w, item.children[0].width() + 8); w = Math.max(
w,
item.label.width() + 8 +
(item.shortcut ? item.shortcut.width() + 4 : 0)
);
} else if ((item instanceof StringFieldMorph) || } else if ((item instanceof StringFieldMorph) ||
(item instanceof ColorPickerMorph) || (item instanceof ColorPickerMorph) ||
(item instanceof SliderMorph)) { (item instanceof SliderMorph)) {
@ -7649,6 +7666,7 @@ MenuMorph.prototype.adjustWidths = function () {
this.children.forEach(function (item) { this.children.forEach(function (item) {
item.silentSetWidth(w); item.silentSetWidth(w);
if (item instanceof MenuItemMorph) { if (item instanceof MenuItemMorph) {
item.fixLayout();
isSelected = (item.image === item.pressImage); isSelected = (item.image === item.pressImage);
item.createBackgrounds(); item.createBackgrounds();
if (isSelected) { if (isSelected) {
@ -7720,6 +7738,24 @@ MenuMorph.prototype.popUpCenteredInWorld = function (world) {
); );
}; };
// MenuMorph submenus
MenuMorph.prototype.closeRootMenu = function () {
if (this.parent instanceof MenuMorph) {
this.parent.closeRootMenu();
} else {
this.destroy();
}
};
MenuMorph.prototype.closeSubmenu = function () {
if (this.submenu) {
this.submenu.destroy();
this.submenu = null;
this.unselectAllItems();
}
};
// MenuMorph keyboard accessibility // MenuMorph keyboard accessibility
MenuMorph.prototype.getFocus = function () { MenuMorph.prototype.getFocus = function () {
@ -7730,18 +7766,25 @@ MenuMorph.prototype.getFocus = function () {
}; };
MenuMorph.prototype.processKeyDown = function (event) { MenuMorph.prototype.processKeyDown = function (event) {
//console.log(event.keyCode); // console.log(event.keyCode);
switch (event.keyCode) { switch (event.keyCode) {
case 13: // 'enter' case 13: // 'enter'
case 32: // 'space' case 32: // 'space'
if (this.selection) { if (this.selection) {
this.selection.mouseClickLeft(); this.selection.mouseClickLeft();
if (this.submenu) {
this.submenu.getFocus();
}
} }
return; return;
case 27: // 'esc' case 27: // 'esc'
return this.destroy(); return this.destroy();
case 37: // 'left arrow'
return this.leaveSubmenu();
case 38: // 'up arrow' case 38: // 'up arrow'
return this.selectUp(); return this.selectUp();
case 39: // 'right arrow'
return this.enterSubmenu();
case 40: // 'down arrow' case 40: // 'down arrow'
return this.selectDown(); return this.selectDown();
default: default:
@ -7805,6 +7848,25 @@ MenuMorph.prototype.selectDown = function () {
this.select(triggers[idx]); this.select(triggers[idx]);
}; };
MenuMorph.prototype.enterSubmenu = function () {
if (this.selection && this.selection.action instanceof MenuMorph) {
this.selection.popUpSubmenu();
if (this.submenu) {
this.submenu.getFocus();
}
}
};
MenuMorph.prototype.leaveSubmenu = function () {
var menu = this.parent;
if (this.parent instanceof MenuMorph) {
menu.submenu = null;
menu.hasFocus = true;
this.destroy();
menu.world.keyboardReceiver = menu;
}
};
MenuMorph.prototype.select = function (aMenuItem) { MenuMorph.prototype.select = function (aMenuItem) {
this.unselectAllItems(); this.unselectAllItems();
aMenuItem.image = aMenuItem.highlightImage; aMenuItem.image = aMenuItem.highlightImage;
@ -9096,6 +9158,7 @@ TriggerMorph.prototype.init = function (
this.labelString = labelString || null; this.labelString = labelString || null;
this.label = null; this.label = null;
this.hint = hint || null; // null, String, or Function this.hint = hint || null; // null, String, or Function
this.schedule = null; // animation slot for displaying hints
this.fontSize = fontSize || MorphicPreferences.menuFontSize; this.fontSize = fontSize || MorphicPreferences.menuFontSize;
this.fontStyle = fontStyle || 'sans-serif'; this.fontStyle = fontStyle || 'sans-serif';
this.highlightColor = new Color(192, 192, 192); this.highlightColor = new Color(192, 192, 192);
@ -9189,6 +9252,9 @@ TriggerMorph.prototype.trigger = function () {
treat it as function property of target and execute it treat it as function property of target and execute it
for selector-like actions for selector-like actions
*/ */
if (this.schedule) {
this.schedule.isActive = false;
}
if (typeof this.target === 'function') { if (typeof this.target === 'function') {
if (typeof this.action === 'function') { if (typeof this.action === 'function') {
this.target.call(this.environment, this.action.call(), this); this.target.call(this.environment, this.action.call(), this);
@ -9208,6 +9274,9 @@ TriggerMorph.prototype.triggerDoubleClick = function () {
// same as trigger() but use doubleClickAction instead of action property // same as trigger() but use doubleClickAction instead of action property
// note that specifying a doubleClickAction is optional // note that specifying a doubleClickAction is optional
if (!this.doubleClickAction) {return; } if (!this.doubleClickAction) {return; }
if (this.schedule) {
this.schedule.isActive = false;
}
if (typeof this.target === 'function') { if (typeof this.target === 'function') {
if (typeof this.doubleClickAction === 'function') { if (typeof this.doubleClickAction === 'function') {
this.target.call( this.target.call(
@ -9241,6 +9310,9 @@ TriggerMorph.prototype.mouseEnter = function () {
TriggerMorph.prototype.mouseLeave = function () { TriggerMorph.prototype.mouseLeave = function () {
this.image = this.normalImage; this.image = this.normalImage;
this.changed(); this.changed();
if (this.schedule) {
this.schedule.isActive = false;
}
if (this.hint) { if (this.hint) {
this.world().hand.destroyTemporaries(); this.world().hand.destroyTemporaries();
} }
@ -9268,15 +9340,17 @@ TriggerMorph.prototype.rootForGrab = function () {
// TriggerMorph bubble help: // TriggerMorph bubble help:
TriggerMorph.prototype.bubbleHelp = function (contents) { TriggerMorph.prototype.bubbleHelp = function (contents) {
var myself = this; var world = this.world(),
this.fps = 2; myself = this;
this.step = function () { this.schedule = new Animation(
if (this.bounds.containsPoint(this.world().hand.position())) { nop,
myself.popUpbubbleHelp(contents); nop,
} 0,
myself.fps = 0; 500,
delete myself.step; nop,
}; function () {myself.popUpbubbleHelp(contents); }
);
world.animations.push(this.schedule);
}; };
TriggerMorph.prototype.popUpbubbleHelp = function (contents) { TriggerMorph.prototype.popUpbubbleHelp = function (contents) {
@ -9313,8 +9387,14 @@ function MenuItemMorph(
color, color,
bold, bold,
italic, italic,
doubleClickAction // optional when used as list morph item doubleClickAction, // optional when used as list morph item
shortcut // optional string, Morph, Canvas or tuple: [icon, string]
) { ) {
// additional properties:
this.shortcutString = shortcut || null;
this.shortcut = null;
// initialize inherited properties:
this.init( this.init(
target, target,
action, action,
@ -9331,31 +9411,58 @@ function MenuItemMorph(
} }
MenuItemMorph.prototype.createLabel = function () { MenuItemMorph.prototype.createLabel = function () {
var icon, lbl, np; var w, h;
if (this.label !== null) { if (this.label) {
this.label.destroy(); this.label.destroy();
} }
if (isString(this.labelString)) { this.label = this.createLabelPart(this.labelString);
this.label = this.createLabelString(this.labelString); this.add(this.label);
} else if (this.labelString instanceof Array) { w = this.label.width();
h = this.label.height();
if (this.shortcut) {
this.shortcut.destroy();
}
if (this.shortcutString) {
this.shortcut = this.createLabelPart(this.shortcutString);
w += this.shortcut.width() + 4;
h = Math.max(h, this.shortcut.height());
this.add(this.shortcut);
}
this.silentSetExtent(new Point(w + 8, h));
this.fixLayout();
};
MenuItemMorph.prototype.fixLayout = function () {
var cntr = this.center();
this.label.setCenter(cntr);
this.label.setLeft(this.left() + 4);
if (this.shortcut) {
this.shortcut.setCenter(cntr);
this.shortcut.setRight(this.right() - 4);
}
};
MenuItemMorph.prototype.createLabelPart = function (source) {
var part, icon, lbl;
if (isString(source)) {
return this.createLabelString(source);
}
if (source instanceof Array) {
// assume its pattern is: [icon, string] // assume its pattern is: [icon, string]
this.label = new Morph(); part = new Morph();
this.label.alpha = 0; // transparent part.alpha = 0; // transparent
icon = this.createIcon(this.labelString[0]); icon = this.createIcon(source[0]);
this.label.add(icon); part.add(icon);
lbl = this.createLabelString(this.labelString[1]); lbl = this.createLabelString(source[1]);
this.label.add(lbl); part.add(lbl);
lbl.setCenter(icon.center()); lbl.setCenter(icon.center());
lbl.setLeft(icon.right() + 4); lbl.setLeft(icon.right() + 4);
this.label.bounds = (icon.bounds.merge(lbl.bounds)); part.bounds = (icon.bounds.merge(lbl.bounds));
this.label.drawNew(); part.drawNew();
} else { // assume it's either a Morph or a Canvas return part;
this.label = this.createIcon(this.labelString);
} }
this.silentSetExtent(this.label.extent().add(new Point(8, 0))); // assume it's either a Morph or a Canvas
np = this.position().add(new Point(4, 0)); return this.createIcon(source);
this.label.bounds = np.extent(this.label.extent());
this.add(this.label);
}; };
MenuItemMorph.prototype.createIcon = function (source) { MenuItemMorph.prototype.createIcon = function (source) {
@ -9393,20 +9500,36 @@ MenuItemMorph.prototype.createLabelString = function (string) {
// MenuItemMorph events: // MenuItemMorph events:
MenuItemMorph.prototype.mouseEnter = function () { MenuItemMorph.prototype.mouseEnter = function () {
var menu = this.parentThatIsA(MenuMorph);
if (this.isShowingSubmenu()) {
return;
}
if (menu) {
menu.closeSubmenu();
}
if (!this.isListItem()) { if (!this.isListItem()) {
this.image = this.highlightImage; this.image = this.highlightImage;
this.changed(); this.changed();
} }
if (this.hint) { if (this.action instanceof MenuMorph) {
this.delaySubmenu();
} else if (this.hint) {
this.bubbleHelp(this.hint); this.bubbleHelp(this.hint);
} }
}; };
MenuItemMorph.prototype.mouseLeave = function () { MenuItemMorph.prototype.mouseLeave = function () {
if (!this.isListItem()) { if (!this.isListItem()) {
this.image = this.normalImage; if (this.isShowingSubmenu()) {
this.image = this.highlightImage;
} else {
this.image = this.normalImage;
}
this.changed(); this.changed();
} }
if (this.schedule) {
this.schedule.isActive = false;
}
if (this.hint) { if (this.hint) {
this.world().hand.destroyTemporaries(); this.world().hand.destroyTemporaries();
} }
@ -9428,11 +9551,15 @@ MenuItemMorph.prototype.mouseMove = function () {
}; };
MenuItemMorph.prototype.mouseClickLeft = function () { MenuItemMorph.prototype.mouseClickLeft = function () {
if (!this.isListItem()) { if (this.action instanceof MenuMorph) {
this.parent.destroy(); this.popUpSubmenu();
this.root().activeMenu = null; } else {
if (!this.isListItem()) {
this.parent.closeRootMenu();
this.world().activeMenu = null;
}
this.trigger();
} }
this.trigger();
}; };
MenuItemMorph.prototype.isListItem = function () { MenuItemMorph.prototype.isListItem = function () {
@ -9449,6 +9576,44 @@ MenuItemMorph.prototype.isSelectedListItem = function () {
return false; return false;
}; };
MenuItemMorph.prototype.isShowingSubmenu = function () {
var menu = this.parentThatIsA(MenuMorph);
if (menu && (this.action instanceof MenuMorph)) {
return menu.submenu === this.action;
}
return false;
};
// MenuItemMorph submenus:
MenuItemMorph.prototype.delaySubmenu = function () {
var world = this.world(),
myself = this;
this.schedule = new Animation(
nop,
nop,
0,
500,
nop,
function () {myself.popUpSubmenu(); }
);
world.animations.push(this.schedule);
};
MenuItemMorph.prototype.popUpSubmenu = function () {
var menu = this.parentThatIsA(MenuMorph);
if (!(this.action instanceof MenuMorph)) {return; }
this.action.drawNew();
this.action.setPosition(this.topRight().subtract(new Point(0, 5)));
this.action.addShadow(new Point(2, 2), 80);
this.action.keepWithin(this.world());
if (this.action.items.length < 1 && !this.action.title) {return; }
menu.add(this.action);
menu.submenu = this.action;
menu.submenu.world = menu.world; // keyboard control
this.action.fullChanged();
};
// FrameMorph ////////////////////////////////////////////////////////// // FrameMorph //////////////////////////////////////////////////////////
// I clip my submorphs at my bounds // I clip my submorphs at my bounds

Wyświetl plik

@ -9,7 +9,7 @@
written by Jens Mönig written by Jens Mönig
jens@moenig.org jens@moenig.org
Copyright (C) 2016 by Jens Mönig Copyright (C) 2017 by Jens Mönig
This file is part of Snap!. This file is part of Snap!.
@ -82,7 +82,7 @@ SpeechBubbleMorph, RingMorph, isNil, FileReader, TableDialogMorph,
BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize, BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph*/ TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph*/
modules.objects = '2016-December-27'; modules.objects = '2017-January-03';
var SpriteMorph; var SpriteMorph;
var StageMorph; var StageMorph;
@ -2272,7 +2272,11 @@ SpriteMorph.prototype.freshPalette = function (category) {
}); });
} }
menu.addItem('find blocks...', function () {myself.searchBlocks(); }); menu.addPair(
'find blocks...',
function () {myself.searchBlocks(); },
'⌘F'
);
if (canHidePrimitives()) { if (canHidePrimitives()) {
menu.addItem( menu.addItem(
'hide primitives', 'hide primitives',

Wyświetl plik

@ -7,7 +7,7 @@
written by Jens Mönig written by Jens Mönig
jens@moenig.org jens@moenig.org
Copyright (C) 2015 by Jens Mönig Copyright (C) 2017 by Jens Mönig
This file is part of Snap!. This file is part of Snap!.
@ -74,7 +74,7 @@ HTMLCanvasElement, fontHeight, SymbolMorph, localize, SpeechBubbleMorph,
ArrowMorph, MenuMorph, isString, isNil, SliderMorph, MorphicPreferences, ArrowMorph, MenuMorph, isString, isNil, SliderMorph, MorphicPreferences,
ScrollFrameMorph*/ ScrollFrameMorph*/
modules.widgets = '2016-July-19'; modules.widgets = '2017-January-03';
var PushButtonMorph; var PushButtonMorph;
var ToggleButtonMorph; var ToggleButtonMorph;
@ -575,6 +575,9 @@ ToggleButtonMorph.prototype.mouseLeave = function () {
this.image = this.normalImage; this.image = this.normalImage;
this.changed(); this.changed();
} }
if (this.schedule) {
this.schedule.isActive = false;
}
if (this.hint) { if (this.hint) {
this.world().hand.destroyTemporaries(); this.world().hand.destroyTemporaries();
} }