From f86a414343e0c493eb044e8f9cb90c2cc6afb2ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20M=C3=B6nig?= Date: Wed, 1 Jun 2016 13:35:54 +0200 Subject: [PATCH] Interactive Toggle Switches for Boolean Slots and Literals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in progress… --- blocks.js | 254 ++++++++++++++++++++++++++++++++++++++++++++-------- history.txt | 1 + objects.js | 37 ++++---- store.js | 31 +++++-- threads.js | 16 ++-- 5 files changed, 266 insertions(+), 73 deletions(-) diff --git a/blocks.js b/blocks.js index 41f35967..5f65ffc1 100644 --- a/blocks.js +++ b/blocks.js @@ -145,11 +145,11 @@ radians, useBlurredShadows, SpeechBubbleMorph, modules, StageMorph, fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph, CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil, -isSnapObject, copy*/ +isSnapObject, copy, PushButtonMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2016-May-30'; +modules.blocks = '2016-June-01'; var SyntaxElementMorph; var BlockMorph; @@ -1275,8 +1275,15 @@ SyntaxElementMorph.prototype.labelPart = function (spec) { part = new ArgMorph('list'); break; case '%b': + part = new BooleanSlotMorph(); + break; case '%boolUE': - part = new BooleanSlotMorph(null, true); + part = new BooleanSlotMorph(); + part.isUnevaluated = true; + break; + case '%bool': + part = new BooleanSlotMorph(true); + part.isStatic = true; break; case '%cmd': part = new CommandSlotMorph(); @@ -1959,14 +1966,14 @@ SyntaxElementMorph.prototype.endLayout = function () { arity: single - %br - user-forced line break - %s - white rectangular type-in slot ("string-type") + %br - user-forced line break + %s - white rectangular type-in slot ("string-type") %txt - white rectangular type-in slot ("text-type") %mlt - white rectangular type-in slot ("multi-line-text-type") - %code - white rectangular type-in slot, monospaced font - %n - white roundish type-in slot ("numerical") + %code - white rectangular type-in slot, monospaced font + %n - white roundish type-in slot ("numerical") %dir - white roundish type-in slot with drop-down for directions - %inst - white roundish type-in slot with drop-down for instruments + %inst - white roundish type-in slot with drop-down for instruments %ida - white roundish type-in slot with drop-down for list indices %idx - white roundish type-in slot for indices incl. "any" %obj - specially drawn slot for object reporters @@ -1984,18 +1991,19 @@ SyntaxElementMorph.prototype.endLayout = function () { %var - chameleon colored rectangular drop-down for variable names %shd - Chameleon colored rectuangular drop-down for shadowed var names %lst - chameleon colored rectangular drop-down for list names - %b - chameleon colored hexagonal slot (for predicates) - %l - list icon - %c - C-shaped command slot, special form for primitives - %cs - C-shaped, auto-reifying, accepts reporter drops - %cl - C-shaped, auto-reifying, rejects reporters + %b - chameleon colored hexagonal slot (for predicates) + %bool - chameleon colored hexagonal slot (for predicates), static + %l - list icon + %c - C-shaped command slot, special form for primitives + %cs - C-shaped, auto-reifying, accepts reporter drops + %cl - C-shaped, auto-reifying, rejects reporters %clr - interactive color slot - %t - inline variable reporter template - %anyUE - white rectangular type-in slot, unevaluated if replaced - %boolUE - chameleon colored hexagonal slot, unevaluated if replaced - %f - round function slot, unevaluated if replaced, - %r - round reporter slot - %p - hexagonal predicate slot + %t - inline variable reporter template + %anyUE - white rectangular type-in slot, unevaluated if replaced + %boolUE - chameleon colored hexagonal slot, unevaluated if replaced + %f - round function slot, unevaluated if replaced, + %r - round reporter slot + %p - hexagonal predicate slot rings: @@ -3194,10 +3202,10 @@ BlockMorph.prototype.alternateBlockColor = function () { this.setColor( this.zebraContrast < 0 ? clr.darker(Math.abs(this.zebraContrast)) : clr.lighter(this.zebraContrast), - true // silently + this.hasLabels() // silently ); } else { - this.setColor(clr, true); // silently + this.setColor(clr, this.hasLabels()); // silently } this.fixLabelColor(); this.fixChildrensBlockColor(true); // has issues if not forced @@ -3250,6 +3258,12 @@ BlockMorph.prototype.setCategory = function (aString) { this.endLayout(); }; +BlockMorph.prototype.hasLabels = function () { + return this.children.some(function (any) { + return any instanceof StringMorph; + }); +}; + // BlockMorph copying BlockMorph.prototype.fullCopy = function (forClone) { @@ -5067,7 +5081,13 @@ RingMorph.prototype.embed = function (aBlock, inputNames) { this.selector = 'reifyPredicate'; slot = this.parts()[0]; slot.silentReplaceInput(slot.contents(), aBlock); - } else { // reporter + } else if (aBlock instanceof BooleanSlotMorph) { + this.isStatic = false; + this.setSpec('%rp %ringparms'); + this.selector = 'reifyPredicate'; + slot = this.parts()[0]; + slot.silentReplaceInput(slot.contents(), aBlock); + } else { // reporter or input slot) this.isStatic = false; this.setSpec('%rr %ringparms'); this.selector = 'reifyReporter'; @@ -7951,7 +7971,16 @@ TemplateSlotMorph.prototype.drawRounded = ReporterBlockMorph %b - Boolean %boolUE - Boolean unevaluated - evaluate returns null + I can be directly edited. When the user clicks on me I toggle + between , and values. + + evaluate returns my value. + + my most important public attributes and accessors are: + + value - user editable contents (Boolean or null) + setContents(Boolean/null) - display the argument (Boolean or null) + */ // BooleanSlotMorph inherits from ArgMorph: @@ -7962,16 +7991,55 @@ BooleanSlotMorph.uber = ArgMorph.prototype; // BooleanSlotMorph instance creation: -function BooleanSlotMorph() { - this.init(); +function BooleanSlotMorph(initialValue) { + this.init(initialValue); } -BooleanSlotMorph.prototype.init = function () { +BooleanSlotMorph.prototype.init = function (initialValue) { + this.value = (typeof initialValue === 'boolean') ? initialValue : null; + this.isUnevaluated = false; BooleanSlotMorph.uber.init.call(this); }; BooleanSlotMorph.prototype.getSpec = function () { - return '%b'; + return this.isUnevaluated ? '%boolUE' : '%b'; +}; + +// BooleanSlotMorph accessing: + +BooleanSlotMorph.prototype.evaluate = function () { + return this.value; +}; + +BooleanSlotMorph.prototype.isEmptySlot = function () { + return this.value === null; +}; + +BooleanSlotMorph.prototype.setContents = function (boolOrNull) { + this.value = boolOrNull; + this.drawNew(); + this.changed(); +}; + +BooleanSlotMorph.prototype.toggleValue = function () { + if (this.isStatic) { + this.setContents(!this.value); + return; + } + switch (this.value) { + case true: + return this.setContents(false); + case false: + return this.setContents(null); + default: + this.setContents(true); + } +}; + +// BooleanSlotMorph events: + +BooleanSlotMorph.prototype.mouseClickLeft = function () { + this.toggleValue(); }; // BooleanSlotMorph drawing: @@ -7982,15 +8050,14 @@ BooleanSlotMorph.prototype.drawNew = function () { (this.fontSize + this.edge * 2) * 2, this.fontSize + this.edge * 2 )); - if (this.parent) { - this.color = this.parent.color; - } + this.color = this.parent ? this.parent.color : new Color(200, 200, 200); this.cachedClr = this.color.toString(); this.cachedClrBright = this.bright(); this.cachedClrDark = this.dark(); this.image = newCanvas(this.extent()); context = this.image.getContext('2d'); - this.drawDiamond(context, true); + this.drawDiamond(context); + this.drawKnob(context); }; BooleanSlotMorph.prototype.drawDiamond = function (context) { @@ -8001,7 +8068,16 @@ BooleanSlotMorph.prototype.drawDiamond = function (context) { gradient; // draw the 'flat' shape: - context.fillStyle = this.color.darker(25).toString(); + switch (this.value) { + case true: + context.fillStyle = 'rgb(0, 200, 0)'; + break; + case false: + context.fillStyle = 'rgb(200, 0, 0)'; + break; + default: + context.fillStyle = this.color.darker(25).toString(); + } context.beginPath(); context.moveTo(0, r); @@ -8097,10 +8173,107 @@ BooleanSlotMorph.prototype.drawDiamond = function (context) { context.stroke(); }; -// BooleanSlotMorph implicit formal parameters: +BooleanSlotMorph.prototype.drawKnob = function (context) { + var w = this.width(), + r = this.height() / 2, + shift = this.edge / 2, + gradient, + x, + y = r, + outline = PushButtonMorph.prototype.outline / 2, + outlineColor = PushButtonMorph.prototype.outlineColor, + color = PushButtonMorph.prototype.color, + contrast = PushButtonMorph.prototype.contrast, + topColor = color.lighter(contrast), + bottomColor = color.darker(contrast); -BooleanSlotMorph.prototype.isEmptySlot = function () { - return true; + // draw the 'flat' shape: + switch (this.value) { + case true: + x = r; + if (!MorphicPreferences.isFlat) { + context.shadowOffsetX = shift; + context.shadowBlur = shift; + context.shadowColor = 'black'; + } + break; + case false: + x = w - r; + break; + default: + return; + } + + context.fillStyle = color.toString(); + context.beginPath(); + context.arc(x, y, r, radians(0), radians(360)); + context.closePath(); + context.fill(); + + if (MorphicPreferences.isFlat) {return; } + + // add 3D-Effect + // outline: + context.shadowOffsetX = 0; + context.shadowBlur = 0; + context.shadowColor = 'black'; + context.lineWidth = outline; + context.strokeStyle = outlineColor.toString(); + context.beginPath(); + context.arc(x, y, r - (outline / 2), radians(0), radians(360)); + context.stroke(); + + // top-left: + gradient = context.createRadialGradient( + x, + y, + r - outline - this.edge, + x, + y, + r - outline + ); + gradient.addColorStop(1, topColor.toString()); + gradient.addColorStop(0, color.toString()); + + context.strokeStyle = gradient; + context.lineCap = 'round'; + context.lineWidth = this.edge; + context.beginPath(); + context.arc( + x, + y, + r - outline - this.edge / 2, + radians(180), + radians(270), + false + ); + context.stroke(); + + // bottom-right: + gradient = context.createRadialGradient( + x, + y, + r - outline - this.edge, + x, + y, + r - outline + ); + gradient.addColorStop(1, bottomColor.toString()); + gradient.addColorStop(0, color.toString()); + + context.strokeStyle = gradient; + context.lineCap = 'round'; + context.lineWidth = this.edge; + context.beginPath(); + context.arc( + x, + y, + r - outline - this.edge / 2, + radians(0), + radians(90), + false + ); + context.stroke(); }; // ArrowMorph ////////////////////////////////////////////////////////// @@ -11628,6 +11801,10 @@ ScriptFocusMorph.prototype.trigger = function () { current.mouseClickLeft(); return; } + if (current instanceof BooleanSlotMorph) { + current.toggleValue(); + return; + } if (current instanceof InputSlotMorph) { if (!current.isReadOnly) { delete this.fps; @@ -11670,6 +11847,10 @@ ScriptFocusMorph.prototype.deleteLastElement = function () { if (current.arrows().children[0].isVisible) { current.removeInput(); } + } else if (current instanceof BooleanSlotMorph) { + if (!current.isStatic) { + current.setContents(null); + } } else if (current instanceof ReporterBlockMorph) { if (!current.isTemplate) { this.lastElement(); @@ -11996,6 +12177,7 @@ ScriptFocusMorph.prototype.items = function () { !(each instanceof TemplateSlotMorph) && (!each.isStatic || each.choices || + each instanceof BooleanSlotMorph || each instanceof RingMorph || each instanceof MultiArgMorph || each instanceof CommandSlotMorph); @@ -12159,7 +12341,7 @@ ScriptFocusMorph.prototype.reactToKeyEvent = function (key) { default: types = this.blockTypes(); if (!(this.element instanceof ScriptsMorph) && - contains(types, 'reporter')) { + types && contains(types, 'reporter')) { vNames = Object.keys(this.element.getVarNamesDict()); } if (types) { diff --git a/history.txt b/history.txt index 746fc9c1..13d1f3e5 100755 --- a/history.txt +++ b/history.txt @@ -2930,3 +2930,4 @@ http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation == v4.0.7.2 ==== * in progress: Retina Display Support, thanks, Bartosz Leper!! +* in progress: Interactive Toggle Switches for Boolean Slots and Literals diff --git a/objects.js b/objects.js index 1d569b99..9eb11e88 100644 --- a/objects.js +++ b/objects.js @@ -80,9 +80,9 @@ document, isNaN, isString, newCanvas, nop, parseFloat, radians, window, modules, IDE_Morph, VariableDialogMorph, HTMLCanvasElement, Context, List, SpeechBubbleMorph, RingMorph, isNil, FileReader, TableDialogMorph, BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize, -TableMorph, TableFrameMorph, normalizeCanvas*/ +TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph*/ -modules.objects = '2016-May-11'; +modules.objects = '2016-June-01'; var SpriteMorph; var StageMorph; @@ -968,15 +968,10 @@ SpriteMorph.prototype.initBlocks = function () { category: 'operators', spec: 'not %b' }, - reportTrue: { + reportBoolean: { type: 'predicate', category: 'operators', - spec: 'true' - }, - reportFalse: { - type: 'predicate', - category: 'operators', - spec: 'false' + spec: '%bool' }, reportJoinWords: { type: 'reporter', @@ -1220,6 +1215,14 @@ SpriteMorph.prototype.initBlockMigrations = function () { receiveClick: { selector: 'receiveInteraction', inputs: [['clicked']] + }, + reportTrue: { + selector: 'reportBoolean', + inputs: [true] + }, + reportFalse: { + selector: 'reportBoolean', + inputs: [false] } }; }; @@ -1292,8 +1295,6 @@ SpriteMorph.prototype.blockAlternatives = { reportGreaterThan: ['reportEquals', 'reportLessThan'], reportAnd: ['reportOr'], reportOr: ['reportAnd'], - reportTrue: ['reportFalse'], - reportFalse: ['reportTrue'], // variables doSetVar: ['doChangeVar'], @@ -1991,9 +1992,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportAnd')); blocks.push(block('reportOr')); blocks.push(block('reportNot')); - blocks.push('-'); - blocks.push(block('reportTrue')); - blocks.push(block('reportFalse')); + blocks.push(block('reportBoolean')); blocks.push('-'); blocks.push(block('reportJoinWords')); blocks.push(block('reportTextSplit')); @@ -4528,11 +4527,7 @@ SpriteMorph.prototype.fullThumbnail = function (extentPoint) { // SpriteMorph Boolean visual representation SpriteMorph.prototype.booleanMorph = function (bool) { - // answer a block which can be shown in watchers, speech bubbles etc. - var block = new ReporterBlockMorph(true); - block.color = SpriteMorph.prototype.blockColor.operators; - block.setSpec(localize(bool.toString())); - return block; + return new BooleanSlotMorph(bool); }; // SpriteMorph nesting @@ -5758,9 +5753,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push(block('reportAnd')); blocks.push(block('reportOr')); blocks.push(block('reportNot')); - blocks.push('-'); - blocks.push(block('reportTrue')); - blocks.push(block('reportFalse')); + blocks.push(block('reportBoolean')); blocks.push('-'); blocks.push(block('reportJoinWords')); blocks.push(block('reportTextSplit')); diff --git a/store.js b/store.js index 25939d25..22d9f863 100644 --- a/store.js +++ b/store.js @@ -56,11 +56,11 @@ Color, List, newCanvas, Costume, Sound, Audio, IDE_Morph, ScriptsMorph, BlockMorph, ArgMorph, InputSlotMorph, TemplateSlotMorph, CommandSlotMorph, FunctionSlotMorph, MultiArgMorph, ColorSlotMorph, nop, CommentMorph, isNil, localize, sizeOf, ArgLabelMorph, SVG_Costume, MorphicPreferences, -SyntaxElementMorph, Variable, isSnapObject, console*/ +SyntaxElementMorph, Variable, isSnapObject, console, BooleanSlotMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2016-May-10'; +modules.store = '2016-June-01'; // XML_Serializer /////////////////////////////////////////////////////// @@ -1134,7 +1134,7 @@ SnapSerializer.prototype.loadInput = function (model, input, block) { SnapSerializer.prototype.loadValue = function (model) { // private - var v, lst, items, el, center, image, name, audio, option, + var v, lst, items, el, center, image, name, audio, option, bool, myself = this; function record() { @@ -1165,7 +1165,14 @@ SnapSerializer.prototype.loadValue = function (model) { throw new Error('expecting a reference id'); case 'l': option = model.childNamed('option'); - return option ? [option.contents] : model.contents; + if (option) { + return [option.contents]; + } + bool = model.childNamed('bool'); + if (bool) { + return this.loadValue(bool); + } + return model.contents; case 'bool': return model.contents === 'true'; case 'list': @@ -1248,7 +1255,14 @@ SnapSerializer.prototype.loadValue = function (model) { } else { el = model.childNamed('l'); if (el) { - v.expression = new InputSlotMorph(el.contents); + bool = el.childNamed('bool'); + if (bool) { + v.expression = new BooleanSlotMorph( + this.loadValue(bool) + ); + } else { + v.expression = new InputSlotMorph(el.contents); + } } } } @@ -1842,6 +1856,13 @@ ArgMorph.prototype.toXML = function () { return ''; // empty by default }; +BooleanSlotMorph.prototype.toXML = function () { + return (typeof this.value === 'boolean') ? + '' + this.value + '' + : ''; + +}; + InputSlotMorph.prototype.toXML = function (serializer) { if (this.constant) { return serializer.format( diff --git a/threads.js b/threads.js index 42045ac6..85beeb83 100644 --- a/threads.js +++ b/threads.js @@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy, isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, TableFrameMorph, isSnapObject*/ -modules.threads = '2016-May-31'; +modules.threads = '2016-June-01'; var ThreadManager; var Process; @@ -149,6 +149,8 @@ function invoke( action.blockSequence(), proc.homeContext ); + } else if (action.evaluate) { + return action.evaluate(); } else { throw new Error('expecting a block or ring but getting ' + action); } @@ -346,9 +348,7 @@ ThreadManager.prototype.doWhen = function (block, stopIt) { } } if (stopIt) {return; } - if ((!block) || - !(pred instanceof ReporterBlockMorph) || - this.findProcess(block) + if ((!block) || this.findProcess(block) ) {return; } try { if (invoke( @@ -2293,12 +2293,8 @@ Process.prototype.isImmutable = function (obj) { type === 'undefined'; }; -Process.prototype.reportTrue = function () { - return true; -}; - -Process.prototype.reportFalse = function () { - return false; +Process.prototype.reportBoolean = function (bool) { + return bool; }; Process.prototype.reportRound = function (n) {