Compare commits

...

25 Commits

Author SHA1 Message Date
59ca9cf463 0.0.52 2018-01-04 13:04:23 -08:00
7da811246c Behaviors Driven Robotics (#178)
* adding avoid crash behavior

* more implementation

* add another simple behavior

* fixed synched motors

* bump common packages
2018-01-04 12:55:30 -08:00
69f8453947 Merge pull request #177 from Microsoft/sounds_bug_fix
Fix stopAllSounds simulator bug
2018-01-04 12:52:17 -08:00
39ba9b81af Initial work 2018-01-04 12:20:31 -08:00
51a14596cd 0.0.51 2018-01-04 11:41:49 -08:00
8518c446cd Merge pull request #176 from Microsoft/iefonticons
Fix font icons on IE, and add package icon on Windows
2018-01-04 11:40:42 -08:00
2f69df0d9d Fix font icons on IE, and add package icon on Windows 2018-01-04 11:39:20 -08:00
1789d0ce21 Lowercase menubar titles. 2018-01-04 10:42:37 -08:00
d0809510c4 Merge pull request #174 from Microsoft/addbetatag
Add beta tag.
2018-01-04 10:14:43 -08:00
6b9c0eaf65 Add beta tag. #150 2018-01-04 10:14:11 -08:00
52bdf94233 Merge pull request #173 from Microsoft/blockiconfont
Add block icon font
2018-01-04 09:57:11 -08:00
fd50ed8f7c Add block icon font 2018-01-04 09:52:12 -08:00
ba0eb93b0f use common packages tests implementation (#171) 2018-01-04 09:23:28 -08:00
9a4ed45797 0.0.50 2018-01-04 09:19:33 -08:00
820fdf3a3c Merge pull request #172 from Microsoft/fixwinicons
Fix check and cancel icons on windows
2018-01-04 09:18:30 -08:00
348d5ffc26 Fix check and cancel icons on windows 2018-01-04 09:17:30 -08:00
95e47a0b25 Merge pull request #169 from Microsoft/screenimgeditor
Initial screen field editor.
2018-01-04 08:37:56 -08:00
c3312ed5d1 Merge pull request #170 from Microsoft/clearcounts
moving motor blocks around
2018-01-04 08:37:39 -08:00
b7cdc7d0fe moving blocks around 2018-01-04 08:25:02 -08:00
dde5a35cd9 Initial screen field editor. Adding Lego designer assets to legoresources 2018-01-03 17:20:35 -08:00
be398d84ee Merge pull request #135 from Microsoft/field_ports
Add an output port field editor
2018-01-03 17:07:34 -08:00
4445acce7a Split editor extensions into multiple module loaded files. Re-using editor/deploy.ts for cmds. Fixing field ports 2018-01-03 16:18:39 -08:00
d6d8b0655b Merge branch 'master' into field_ports 2018-01-03 15:12:35 -08:00
216aa1ddaf Fix save icon 2018-01-03 15:07:45 -08:00
a1b059171b Add a output port field editor 2017-12-24 17:46:58 -08:00
71 changed files with 770 additions and 450 deletions

View File

@ -2,7 +2,7 @@
import * as fs from 'fs';
require("./editor")
require("./editor/deploy")
declare namespace pxt.editor {
function deployCoreAsync(resp: pxtc.CompileResult, disconnect?: boolean): Promise<void>;

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont";
src: url("iconfont.eot?6e1ef95090bc1e1acc3a7e6bb86172de?#iefix") format("embedded-opentype"),
url("iconfont.woff2?6e1ef95090bc1e1acc3a7e6bb86172de") format("woff2"),
url("iconfont.woff?6e1ef95090bc1e1acc3a7e6bb86172de") format("woff");
src: url("iconfont.eot?73552ec404b3a3d3769a3f04fa58c2c4?#iefix") format("embedded-opentype"),
url("iconfont.woff2?73552ec404b3a3d3769a3f04fa58c2c4") format("woff2"),
url("iconfont.woff?73552ec404b3a3d3769a3f04fa58c2c4") format("woff");
}
.icon {
@ -67,21 +67,24 @@ url("iconfont.woff?6e1ef95090bc1e1acc3a7e6bb86172de") format("woff");
.icon-variables:before {
content: "\f111";
}
.icon-cancel:before {
.icon-advancedcollapsed:before {
content: "\f112";
}
.icon-check:before {
.icon-advancedexpanded:before {
content: "\f113";
}
.icon-download:before {
.icon-cancel:before {
content: "\f114";
}
.icon-save:before {
.icon-check:before {
content: "\f115";
}
.icon-advancedcollapsed:before {
.icon-download:before {
content: "\f116";
}
.icon-advancedexpanded:before {
.icon-save:before {
content: "\f117";
}
.icon-blocks:before {
content: "\f118";
}

Binary file not shown.

View File

@ -21,7 +21,7 @@
horiz-adv-x="23.578015492438215" d=" M17 0H6.4A5.901881224640355 5.901881224640355 0 0 0 5.1 0.3H2.3A1.283659166359277 1.283659166359277 0 0 0 1 1.6V4.5H0.5A0.5016599040944301 0.5016599040944301 0 0 0 0 5V35.4A0.5016599040944301 0.5016599040944301 0 0 0 0.5 35.9H1V38.7A1.283659166359277 1.283659166359277 0 0 0 2.3 40H10.6V39.6H12.8V40H21.2A1.283659166359277 1.283659166359277 0 0 0 22.5 38.7V35.9H23.1A0.5016599040944301 0.5016599040944301 0 0 0 23.6 35.4V5.1A0.5016599040944301 0.5016599040944301 0 0 0 23.1 4.6H22.5V1.6A1.283659166359277 1.283659166359277 0 0 0 21.2 0.4H18.5C18.4 0.1 17.2 0 17 0zM6.2 34.5L3.7 33.8A1.0033198081888601 1.0033198081888601 0 0 1 3.4 33.7A0.2360752489856142 0.2360752489856142 0 0 1 3.3 33.5A0.20656584286241245 0.20656584286241245 0 0 1 3.4 33.3L4.1 32.8A7.657690888970861 7.657690888970861 0 0 1 3.1 30A8.51346366654371 8.51346366654371 0 0 1 4.8 23.5C5.8 24.2 6.5 24.7 6.5 24.7A7.377351530800443 7.377351530800443 0 0 0 5.4 26.9A6.300258207303577 6.300258207303577 0 0 0 6 31.8L6.5 31.5H6.5L6.6 31.5A0.3541128734784213 0.3541128734784213 0 0 1 6.8 31.4A0.17705643673921065 0.17705643673921065 0 0 1 7 31.5A0.6344522316488379 0.6344522316488379 0 0 1 7 32L6.2 34.5zM17.5 31.8H17.5A6.300258207303577 6.300258207303577 0 0 0 18 26.9A7.510143858354851 7.510143858354851 0 0 0 17 24.7L17.1 24.6L18.6 23.5A8.631501291036518 8.631501291036518 0 0 1 20.3 30A7.657690888970861 7.657690888970861 0 0 1 19.3 32.8L19.8 33.1L20 33.2A0.22132054592401326 0.22132054592401326 0 0 1 20.1 33.4A0.26558465510881596 0.26558465510881596 0 0 1 20 33.6A1.0033198081888601 1.0033198081888601 0 0 1 19.7 33.7L17.2 34.5S17.1 34.1 17 33.6S16.6 32.3 16.5 32A0.619697528587237 0.619697528587237 0 0 1 16.5 31.5A0.17705643673921065 0.17705643673921065 0 0 1 16.6 31.4A0.48690520103282936 0.48690520103282936 0 0 1 16.8 31.4L16.8 31.4L17.4 31.7zM11.7 30.6A1.9918849133161198 1.9918849133161198 0 1 1 13.7 28.6A1.9918849133161198 1.9918849133161198 0 0 1 11.7 30.6H11.7z" />
<glyph glyph-name="addpackage"
unicode="&#xF105;"
horiz-adv-x="40" d=" M4.3 19.1C4.3 10.5 11.4 3.5 20 3.5C28.6 3.5 35.7 10.5 35.7 19.1C35.7 27.8 28.6 34.8 20 34.8C11.4 34.8 4.3 27.8 4.3 19.1z M30.4 20.9L21.7 20.9L21.7 29.6L18.3 29.6L18.3 20.9L9.6 20.9L9.6 17.4L18.3 17.4L18.3 8.7L21.7 8.7L21.7 17.4L30.4 17.4z" />
horiz-adv-x="40" d=" M20 35.7C11.3 35.7 4.3 28.7 4.3 20S11.3 4.3 20 4.3S35.7 11.3 35.7 20S28.7 35.7 20 35.7zM30.4 18.3H21.7V9.6H18.3V18.3H9.6V21.7H18.3V30.4H21.7V21.7H30.4V18.3z" />
<glyph glyph-name="brick"
unicode="&#xF106;"
horiz-adv-x="40" d=" M34.3 0.9H5.7V39.1H34.4V0.9zM10.4 32.7V18.4H29.6V32.7H10.4z" />
@ -58,24 +58,27 @@
<glyph glyph-name="variables"
unicode="&#xF111;"
horiz-adv-x="40" d=" M36.8 12V7.8H3.4V12H36.8z M36.8 22.1V17.9H3.4V22.1H36.8z M36.7 32.2V28H3.3V32.2H36.7z" />
<glyph glyph-name="cancel"
unicode="&#xF112;"
horiz-adv-x="40" d=" M4285.9 23284.1H4308.1V23280.4H4285.9V23284.1z M4285.9 23302.6H4289.6V23280.4H4285.9V23302.6z" />
<glyph glyph-name="check"
unicode="&#xF113;"
horiz-adv-x="40" d=" M4209.2 23456H4231.3V23452.3H4209.2V23456z M4201.2 23463.4H4204.9V23452.3H4201.2V23463.4z" />
<glyph glyph-name="download"
unicode="&#xF114;"
horiz-adv-x="40" d=" M5.2 15.7H36.5V1.7H5.2V15.7z M28.5 24.2L26.1 26.6L22.6 23.1L22.6 36.5L19.1 36.5L19.1 23.5L16 26.6L13.6 24.2L20.9 16.7L21 16.9L21.2 16.7z" />
<glyph glyph-name="save"
unicode="&#xF115;"
horiz-adv-x="40" d=" M2.3 20.1V36.6C2.3 37.8 2.4 38 3.7 38H11.5C12.7 38 12.9 37.8 12.9 36.6V28.6C12.9 27.6 13 27.4 14.1 27.4H25.9C27 27.4 27.1 27.6 27.1 28.6V36.8C27.1 37.8 27.3 38 28.3 38C30.8 38 30.8 38 32.3 36.4C34.1 34.7 35.7 33.1 37.4 31.4C37.7 31 37.9 30.5 37.9 30V3.6C37.9 2.5 37.7 2.2 36.7 2.2H3.5C2.4 2.2 2.3 2.3 2.3 3.6C2.3 9.1 2.3 14.7 2.3 20.1zM20.2 6H33C34.3 6 34.4 6.2 34.4 7.4V22.5C34.4 23.6 34.3 23.9 33 23.9H7.1C6.1 23.9 5.7 23.7 5.7 22.5V7.4C5.7 6.2 5.9 6 7.1 6H20.2z M24.7 34.5C24.7 33.6 24.7 32.8 24.7 31.9C24.7 31 24.3 30.9 23.7 30.9C23.1 30.9 22.4 30.9 22.1 30.9S21 31 21 31.7V31.9V36.9C21 37.3 21.2 37.8 21.9 38C21.9 38 21.9 38 22.1 38C22.6 38 23.3 38 23.8 38C24.3 38 24.9 37.6 24.9 37.1C24.9 37.1 24.9 37.1 24.9 36.9C24.7 36.3 24.7 35.2 24.7 34.5z M5.1 25H35.4V4.8H5.1V25z M6.2 24.1H34.5V5.9H6.2V24.1z" />
<glyph glyph-name="advancedcollapsed"
unicode="&#xF116;"
unicode="&#xF112;"
horiz-adv-x="40" d=" M39.7 28.2L36.2 31.5L20 15.3L3.8 31.5L0.3 28.2L18.3 10.3L18.3 10.3L20 8.5L20.5 9L20.5 9z" />
<glyph glyph-name="advancedexpanded"
unicode="&#xF117;"
unicode="&#xF113;"
horiz-adv-x="40" d=" M39.3 12L21.7 29.6L21.7 29.6L20 31.3L19.5 30.8L19.5 30.8L0.7 12L4 8.7L20 24.7L36 8.7z" />
<glyph glyph-name="cancel"
unicode="&#xF114;"
horiz-adv-x="40" d=" M33 29.6L29.4 33.2L20.2 24L11 33.2L7.3 29.6L16.5 20.3L7.3 11.1L11 7.3L20.2 16.5L29.4 7.3L33 11.1L23.8 20.3z" />
<glyph glyph-name="check"
unicode="&#xF115;"
horiz-adv-x="40" d=" M33.7 32.9L15.3 14.4L7.5 22.3L3.8 18.4L11.7 10.8L11.7 10.8L15.3 7.1L37.4 29.2z" />
<glyph glyph-name="download"
unicode="&#xF116;"
horiz-adv-x="40" d=" M5.2 15.7H36.5V1.7H5.2V15.7z M28.5 24.2L26.1 26.6L22.6 23.1L22.6 36.5L19.1 36.5L19.1 23.5L16 26.6L13.6 24.2L20.9 16.7L21 16.9L21.2 16.7z" />
<glyph glyph-name="save"
unicode="&#xF117;"
horiz-adv-x="40" d=" M25 34.5C25 33.6 25 32.8 25 31.9C25 31 24.7 30.9 24 30.9C23.5 30.9 22.8 30.9 22.4 30.9C22.1 30.9 21.4 31 21.4 31.7V31.9V36.9C21.4 37.3 21.6 37.8 22.3 38C22.3 38 22.3 38 22.4 38C23 38 23.7 38 24.2 38C24.7 38 25.2 37.6 25.2 37.1C25.2 37.1 25.2 37.1 25.2 36.9C25 36.3 25 35.2 25 34.5z M37.6 31.2C35.8 32.9 34.3 34.5 32.5 36.3C31 37.8 31 37.8 28.5 37.8C27.5 37.8 27.3 37.6 27.3 36.6V28.4C27.3 27.4 27.1 27.2 26.1 27.2H14.3C13.2 27.2 13 27.4 13 28.4V36.4C13 37.6 12.9 37.8 11.7 37.8H3.8C2.6 37.8 2.4 37.6 2.4 36.4V19.9C2.4 14.3 2.4 8.9 2.4 3.2C2.4 2 2.6 1.8 3.7 1.8H36.9C37.9 1.8 38.1 2.2 38.1 3.2V29.6C38.3 30.5 37.9 30.9 37.6 31.2zM33.7 6.7H7.1V23H33.7V6.7z" />
<glyph glyph-name="blocks"
unicode="&#xF118;"
horiz-adv-x="40" d=" M10.9 23H39V16.9H10.9V23z M39.2 27.1L39.2 33L0.9 33L0.9 31.1L0.9 27.1L0.9 12.9L0.9 7L39.2 7L39.2 12.9L6.9 12.9L6.9 27.1z" />
</font>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

133
editor/deploy.ts Normal file
View File

@ -0,0 +1,133 @@
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
import UF2 = pxtc.UF2;
export let ev3: pxt.editor.Ev3Wrapper
export function debug() {
return initAsync()
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
}
function hf2Async() {
return pxt.HF2.mkPacketIOAsync()
.then(h => {
let w = new pxt.editor.Ev3Wrapper(h)
ev3 = w
return w.reconnectAsync(true)
.then(() => w)
})
}
let noHID = false
let initPromise: Promise<pxt.editor.Ev3Wrapper>
export function initAsync() {
if (initPromise)
return initPromise
let canHID = false
if (pxt.U.isNodeJS) {
canHID = true
} else {
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
if (pxt.Cloud.isLocalHost() && pxt.Cloud.localToken && !forceHexDownload)
canHID = true
}
if (noHID)
canHID = false
if (canHID) {
initPromise = hf2Async()
.catch(err => {
initPromise = null
noHID = true
return Promise.reject(err)
})
} else {
noHID = true
initPromise = Promise.reject(new Error("no HID"))
}
return initPromise
}
// this comes from aux/pxt.lms
const rbfTemplate = `
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
`
export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false) {
let w: pxt.editor.Ev3Wrapper
let filename = resp.downloadFileBaseName || "pxt"
filename = filename.replace(/^lego-/, "")
let fspath = "../prjs/BrkProg_SAVE/"
let elfPath = fspath + filename + ".elf"
let rbfPath = fspath + filename + ".rbf"
let rbfHex = rbfTemplate
.replace(/\s+/g, "")
.replace("XX", pxt.U.toHex(pxt.U.stringToUint8Array(elfPath)))
let rbfBIN = pxt.U.fromHex(rbfHex)
pxt.HF2.write16(rbfBIN, 4, rbfBIN.length)
let origElfUF2 = UF2.parseFile(pxt.U.stringToUint8Array(atob(resp.outfiles[pxt.outputName()])))
let mkFile = (ext: string, data: Uint8Array = null) => {
let f = UF2.newBlockFile()
f.filename = "Projects/" + filename + ext
if (data)
UF2.writeBytes(f, 0, data)
return f
}
let elfUF2 = mkFile(".elf")
for (let b of origElfUF2) {
UF2.writeBytes(elfUF2, b.targetAddr, b.data)
}
let r = UF2.concatFiles([elfUF2, mkFile(".rbf", rbfBIN)])
let data = UF2.serializeFile(r)
resp.outfiles[pxtc.BINARY_UF2] = btoa(data)
let saveUF2Async = () => {
if (isCli || !pxt.commands.saveOnlyAsync) {
return Promise.resolve()
} else {
return pxt.commands.saveOnlyAsync(resp)
}
}
if (noHID) return saveUF2Async()
return initAsync()
.then(w_ => {
w = w_
if (w.isStreaming)
pxt.U.userError("please stop the program first")
return w.stopAsync()
})
.then(() => w.rmAsync(elfPath))
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
.then(() => w.flashAsync(rbfPath, rbfBIN))
.then(() => w.runAsync(rbfPath))
.then(() => {
if (isCli)
return w.disconnectAsync()
else
return Promise.resolve()
//return Promise.delay(1000).then(() => w.dmesgAsync())
}).catch(e => {
// if we failed to initalize, retry
if (noHID)
return saveUF2Async()
else
return Promise.reject(e)
})
}

View File

@ -1,242 +1,120 @@
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts" />
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
// When require()d from node, bind the global pxt namespace
namespace pxt {
export const dummyExport = 1;
import { deployCoreAsync, initAsync } from "./deploy";
import { FieldPorts } from "./field_ports";
import { FieldImages } from "./field_images";
pxt.editor.initExtensionsAsync = function(opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
pxt.debug('loading pxt-ev3 target extensions...')
updateBlocklyShape();
const res: pxt.editor.ExtensionResult = {
fieldEditors: [{
selector: "ports",
editor: FieldPorts
}, {
selector: "images",
editor: FieldImages
}],
deployCoreAsync
};
initAsync().catch(e => {
// probably no HID - we'll try this again upon deployment
})
return Promise.resolve<pxt.editor.ExtensionResult>(res);
}
eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")
namespace pxt.editor {
import UF2 = pxtc.UF2;
export let ev3: Ev3Wrapper
export function debug() {
return initAsync()
.then(w => w.downloadFileAsync("/tmp/dmesg.txt", v => console.log(pxt.Util.uint8ArrayToString(v))))
}
// this comes from aux/pxt.lms
const rbfTemplate = `
4c45474f580000006d000100000000001c000000000000000e000000821b038405018130813e8053
74617274696e672e2e2e0084006080XX00448581644886488405018130813e80427965210084000a
`
function hf2Async() {
return pxt.HF2.mkPacketIOAsync()
.then(h => {
let w = new Ev3Wrapper(h)
ev3 = w
return w.reconnectAsync(true)
.then(() => w)
})
}
let noHID = false
let initPromise: Promise<Ev3Wrapper>
function initAsync() {
if (initPromise)
return initPromise
let canHID = false
if (U.isNodeJS) {
canHID = true
} else {
const forceHexDownload = /forceHexDownload/i.test(window.location.href);
if (Cloud.isLocalHost() && Cloud.localToken && !forceHexDownload)
canHID = true
}
if (noHID)
canHID = false
if (canHID) {
initPromise = hf2Async()
.catch(err => {
initPromise = null
noHID = true
return Promise.reject(err)
})
} else {
noHID = true
initPromise = Promise.reject(new Error("no HID"))
}
return initPromise
}
export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false) {
let w: Ev3Wrapper
let filename = resp.downloadFileBaseName || "pxt"
filename = filename.replace(/^lego-/, "")
let fspath = "../prjs/BrkProg_SAVE/"
let elfPath = fspath + filename + ".elf"
let rbfPath = fspath + filename + ".rbf"
let rbfHex = rbfTemplate
.replace(/\s+/g, "")
.replace("XX", U.toHex(U.stringToUint8Array(elfPath)))
let rbfBIN = U.fromHex(rbfHex)
HF2.write16(rbfBIN, 4, rbfBIN.length)
let origElfUF2 = UF2.parseFile(U.stringToUint8Array(atob(resp.outfiles[pxt.outputName()])))
let mkFile = (ext: string, data: Uint8Array = null) => {
let f = UF2.newBlockFile()
f.filename = "Projects/" + filename + ext
if (data)
UF2.writeBytes(f, 0, data)
return f
}
let elfUF2 = mkFile(".elf")
for (let b of origElfUF2) {
UF2.writeBytes(elfUF2, b.targetAddr, b.data)
}
let r = UF2.concatFiles([elfUF2, mkFile(".rbf", rbfBIN)])
let data = UF2.serializeFile(r)
resp.outfiles[pxtc.BINARY_UF2] = btoa(data)
let saveUF2Async = () => {
if (isCli || !pxt.commands.saveOnlyAsync) {
return Promise.resolve()
} else {
return pxt.commands.saveOnlyAsync(resp)
}
}
if (noHID) return saveUF2Async()
return initAsync()
.then(w_ => {
w = w_
if (w.isStreaming)
U.userError("please stop the program first")
return w.stopAsync()
})
.then(() => w.rmAsync(elfPath))
.then(() => w.flashAsync(elfPath, UF2.readBytes(origElfUF2, 0, origElfUF2.length * 256)))
.then(() => w.flashAsync(rbfPath, rbfBIN))
.then(() => w.runAsync(rbfPath))
.then(() => {
if (isCli)
return w.disconnectAsync()
else
return Promise.resolve()
//return Promise.delay(1000).then(() => w.dmesgAsync())
}).catch(e => {
// if we failed to initalize, retry
if (noHID)
return saveUF2Async()
else
return Promise.reject(e)
})
}
/**
* Update the shape of Blockly blocks with square corners
*/
function updateBlocklyShape() {
/**
* Update the shape of Blockly blocks with square corners
* Rounded corner radius.
* @const
*/
function updateBlocklyShape() {
(Blockly.BlockSvg as any).CORNER_RADIUS = 0 * (Blockly.BlockSvg as any).GRID_UNIT;
/**
* Rounded corner radius.
* @const
*/
(Blockly.BlockSvg as any).CORNER_RADIUS = 0 * (Blockly.BlockSvg as any).GRID_UNIT;
/**
* Inner space between edge of statement input and notch.
* @const
*/
(Blockly.BlockSvg as any).STATEMENT_INPUT_INNER_SPACE = 3 * (Blockly.BlockSvg as any).GRID_UNIT;
/**
* SVG path for drawing next/previous notch from left to right.
* @const
*/
(Blockly.BlockSvg as any).NOTCH_PATH_LEFT = (
'l 8,8 ' +
'h 16 ' +
'l 8,-8 '
);
/**
* Inner space between edge of statement input and notch.
* @const
*/
(Blockly.BlockSvg as any).STATEMENT_INPUT_INNER_SPACE = 3 * (Blockly.BlockSvg as any).GRID_UNIT;
/**
* SVG path for drawing next/previous notch from left to right.
* @const
*/
(Blockly.BlockSvg as any).NOTCH_PATH_LEFT = (
'l 8,8 ' +
'h 16 ' +
'l 8,-8 '
);
/**
* SVG path for drawing next/previous notch from right to left.
* @const
*/
(Blockly.BlockSvg as any).NOTCH_PATH_RIGHT = (
'l -8,8 ' +
'h -16 ' +
'l -8,-8 '
);
/**
* SVG path for drawing next/previous notch from right to left.
* @const
*/
(Blockly.BlockSvg as any).NOTCH_PATH_RIGHT = (
'l -8,8 ' +
'h -16 ' +
'l -8,-8 '
);
/**
* SVG start point for drawing the top-left corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_LEFT_CORNER_START =
'm 0,' + 0;
/**
* SVG start point for drawing the top-left corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_LEFT_CORNER_START =
'm 0,' + 0;
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_LEFT_CORNER =
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0 ';
/**
* SVG path for drawing the rounded top-left corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_LEFT_CORNER =
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0 ';
/**
* SVG path for drawing the rounded top-right corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_RIGHT_CORNER =
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS;
/**
* SVG path for drawing the rounded top-right corner.
* @const
*/
(Blockly.BlockSvg as any).TOP_RIGHT_CORNER =
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS;
/**
* SVG path for drawing the rounded bottom-right corner.
* @const
*/
(Blockly.BlockSvg as any).BOTTOM_RIGHT_CORNER =
'l 0,' + (Blockly.BlockSvg as any).CORNER_RADIUS;
/**
* SVG path for drawing the rounded bottom-right corner.
* @const
*/
(Blockly.BlockSvg as any).BOTTOM_RIGHT_CORNER =
'l 0,' + (Blockly.BlockSvg as any).CORNER_RADIUS;
/**
* SVG path for drawing the rounded bottom-left corner.
* @const
*/
(Blockly.BlockSvg as any).BOTTOM_LEFT_CORNER =
'l -' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0';
/**
* SVG path for drawing the rounded bottom-left corner.
* @const
*/
(Blockly.BlockSvg as any).BOTTOM_LEFT_CORNER =
'l -' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',0';
/**
* SVG path for drawing the top-left corner of a statement input.
* @const
*/
(Blockly.BlockSvg as any).INNER_TOP_LEFT_CORNER =
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',-' + 0;
/**
* SVG path for drawing the top-left corner of a statement input.
* @const
*/
(Blockly.BlockSvg as any).INNER_TOP_LEFT_CORNER =
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',-' + 0;
/**
* SVG path for drawing the bottom-left corner of a statement input.
* Includes the rounded inside corner.
* @const
*/
(Blockly.BlockSvg as any).INNER_BOTTOM_LEFT_CORNER =
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS * 2 +
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',' + 0;
/**
* SVG path for drawing the bottom-left corner of a statement input.
* Includes the rounded inside corner.
* @const
*/
(Blockly.BlockSvg as any).INNER_BOTTOM_LEFT_CORNER =
'l ' + 0 + ',' + (Blockly.BlockSvg as any).CORNER_RADIUS * 2 +
'l ' + (Blockly.BlockSvg as any).CORNER_RADIUS + ',' + 0;
}
initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise<pxt.editor.ExtensionResult> {
pxt.debug('loading pxt-ev3 target extensions...')
updateBlocklyShape();
const res: pxt.editor.ExtensionResult = {
deployCoreAsync,
};
initAsync().catch(e => {
// probably no HID - we'll try this again upon deployment
})
return Promise.resolve<pxt.editor.ExtensionResult>(res);
}
}
// When require()d from node, bind the global pxt namespace
// namespace pxt {
// export const dummyExport = 1;
// }
// eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")

114
editor/field_images.ts Normal file
View File

@ -0,0 +1,114 @@
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtblocks.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
export interface FieldImagesOptions extends pxtblockly.FieldImageDropdownOptions {
}
export class FieldImages extends pxtblockly.FieldImageDropdown implements Blockly.FieldCustom {
public isFieldCustom_ = true;
constructor(text: string, options: FieldImagesOptions, validator?: Function) {
super(text, options, validator);
}
/**
* Create a dropdown menu under the text.
* @private
*/
public showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
const options = this.getOptions();
for (let i = 0, option: any; option = options[i]; i++) {
let content = (options[i] as any)[0]; // Human-readable text or image.
const value = (options[i] as any)[1]; // Language-neutral value.
// Icons with the type property placeholder take up space but don't have any functionality
// Use for special-case layouts
if (content.type == 'placeholder') {
let placeholder = document.createElement('span');
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
placeholder.style.width = content.width + 'px';
placeholder.style.height = content.height + 'px';
contentDiv.appendChild(placeholder);
continue;
}
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropDownButton');
button.title = content.alt;
if ((this as any).columns_) {
button.style.width = (((this as any).width_ / (this as any).columns_) - 8) + 'px';
//button.style.height = ((this.width_ / this.columns_) - 8) + 'px';
} else {
button.style.width = content.width + 'px';
button.style.height = content.height + 'px';
}
let backgroundColor = this.sourceBlock_.getColour();
if (value == this.getValue()) {
// This icon is selected, show it in a different colour
backgroundColor = this.sourceBlock_.getColourTertiary();
button.setAttribute('aria-selected', 'true');
}
button.style.backgroundColor = backgroundColor;
button.style.borderColor = this.sourceBlock_.getColourTertiary();
Blockly.bindEvent_(button, 'click', this, (this as any).buttonClick_);
Blockly.bindEvent_(button, 'mouseup', this, (this as any).buttonClick_);
// These are applied manually instead of using the :hover pseudoclass
// because Android has a bad long press "helper" menu and green highlight
// that we must prevent with ontouchstart preventDefault
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
e.preventDefault();
});
Blockly.bindEvent_(button, 'mouseover', button, function () {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
contentDiv.setAttribute('aria-activedescendant', this.id);
});
Blockly.bindEvent_(button, 'mouseout', button, function () {
this.setAttribute('class', 'blocklyDropDownButton');
contentDiv.removeAttribute('aria-activedescendant');
});
let buttonImg = document.createElement('img');
buttonImg.src = content.src;
//buttonImg.alt = icon.alt;
// Upon click/touch, we will be able to get the clicked element as e.target
// Store a data attribute on all possible click targets so we can match it to the icon.
button.setAttribute('data-value', value);
buttonImg.setAttribute('data-value', value);
button.appendChild(buttonImg);
contentDiv.appendChild(button);
}
contentDiv.style.width = (this as any).width_ + 'px';
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
// Calculate positioning based on the field position.
var scale = this.sourceBlock_.workspace.scale;
var bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
var position = this.fieldGroup_.getBoundingClientRect();
var primaryX = position.left + bBox.width / 2;
var primaryY = position.top + bBox.height;
var secondaryX = primaryX;
var secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
(this as any).onHide_.bind(this));
}
}

147
editor/field_ports.ts Normal file
View File

@ -0,0 +1,147 @@
/// <reference path="../node_modules/pxt-core/localtypings/blockly.d.ts"/>
/// <reference path="../node_modules/pxt-core/built/pxtsim.d.ts"/>
export interface FieldPortsOptions extends Blockly.FieldCustomDropdownOptions {
columns?: string;
width?: string;
}
export class FieldPorts extends Blockly.FieldDropdown implements Blockly.FieldCustom {
public isFieldCustom_ = true;
// Width in pixels
private width_: number;
// Columns in grid
private columns_: number;
private savedPrimary_: string;
constructor(text: string, options: FieldPortsOptions, validator?: Function) {
super(options.data);
this.columns_ = parseInt(options.columns) || 4;
this.width_ = parseInt(options.width) || 300;
}
/**
* Create a dropdown menu under the text.
* @private
*/
public showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv();
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
const options = this.getOptions();
for (let i = 0, option: any; option = options[i]; i++) {
let content = (options[i] as any)[0]; // Human-readable text or image.
const value = (options[i] as any)[1]; // Language-neutral value.
// Icons with the type property placeholder take up space but don't have any functionality
// Use for special-case layouts
if (content.type == 'placeholder') {
let placeholder = document.createElement('span');
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
placeholder.style.width = content.width + 'px';
placeholder.style.height = content.height + 'px';
contentDiv.appendChild(placeholder);
continue;
}
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropDownButton');
button.title = content.alt;
if (this.columns_) {
button.style.width = ((this.width_ / this.columns_) - 8) + 'px';
button.style.height = ((this.width_ / this.columns_) - 8) + 'px';
} else {
button.style.width = content.width + 'px';
button.style.height = content.height + 'px';
}
let backgroundColor = this.sourceBlock_.getColour();
if (value == this.getValue()) {
// This icon is selected, show it in a different colour
backgroundColor = this.sourceBlock_.getColourTertiary();
button.setAttribute('aria-selected', 'true');
}
button.style.backgroundColor = backgroundColor;
button.style.borderColor = this.sourceBlock_.getColourTertiary();
Blockly.bindEvent_(button, 'click', this, this.buttonClick_);
Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_);
// These are applied manually instead of using the :hover pseudoclass
// because Android has a bad long press "helper" menu and green highlight
// that we must prevent with ontouchstart preventDefault
Blockly.bindEvent_(button, 'mousedown', button, function (e) {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
e.preventDefault();
});
Blockly.bindEvent_(button, 'mouseover', button, function () {
this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
contentDiv.setAttribute('aria-activedescendant', this.id);
});
Blockly.bindEvent_(button, 'mouseout', button, function () {
this.setAttribute('class', 'blocklyDropDownButton');
contentDiv.removeAttribute('aria-activedescendant');
});
let buttonImg = document.createElement('img');
buttonImg.src = content.src;
//buttonImg.alt = icon.alt;
// Upon click/touch, we will be able to get the clicked element as e.target
// Store a data attribute on all possible click targets so we can match it to the icon.
button.setAttribute('data-value', value);
buttonImg.setAttribute('data-value', value);
button.appendChild(buttonImg);
contentDiv.appendChild(button);
}
contentDiv.style.width = this.width_ + 'px';
dropdownDiv.appendChild(contentDiv);
Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary());
// Calculate positioning based on the field position.
var scale = this.sourceBlock_.workspace.scale;
var bBox = { width: this.size_.width, height: this.size_.height };
bBox.width *= scale;
bBox.height *= scale;
var position = this.fieldGroup_.getBoundingClientRect();
var primaryX = position.left + bBox.width / 2;
var primaryY = position.top + bBox.height;
var secondaryX = primaryX;
var secondaryY = position.top;
// Set bounds to workspace; show the drop-down.
(Blockly.DropDownDiv as any).setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode);
(Blockly.DropDownDiv as any).show(this, primaryX, primaryY, secondaryX, secondaryY,
this.onHide_.bind(this));
}
/**
* Callback for when a button is clicked inside the drop-down.
* Should be bound to the FieldIconMenu.
* @param {Event} e DOM event for the click/touch
* @private
*/
private buttonClick_ = function (e: any) {
let value = e.target.getAttribute('data-value');
this.setValue(value);
Blockly.DropDownDiv.hide();
};
/**
* Callback for when the drop-down is hidden.
*/
private onHide_ = function () {
Blockly.DropDownDiv.content_.removeAttribute('role');
Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup');
Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant');
};
}

View File

@ -1,12 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"noImplicitAny": true,
"noImplicitAny": false,
"noImplicitReturns": true,
"declaration": true,
"out": "../built/editor.js",
"module": "commonjs",
"outDir": "../built/editor",
"rootDir": ".",
"newLine": "LF",
"sourceMap": false
"sourceMap": false,
"allowSyntheticDefaultImports": true,
"declaration": true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

View File

@ -0,0 +1,14 @@
{
"behaviors": "Behavior drive blocks",
"behaviors.Behavior": "A behavior",
"behaviors.BehaviorManager": "A manager for behaviors",
"behaviors.BehaviorManager.add": "Adds a new behavior to the behavior manager",
"behaviors.BehaviorManager.add|param|behavior": "the behavior to add",
"behaviors.BehaviorManager.start": "Starts the behavior control loop",
"behaviors.BehaviorManager.stop": "Stops the execution loop",
"behaviors.addBehavior": "Adds the behavior and starts it",
"behaviors.addBehavior|param|behavior": "a behavior",
"behaviors.avoidCrash": "A behavior that stops all motors if the sensor distance get too short",
"behaviors.driveForward": "A behavior that turns on the motors to the specified speed",
"behaviors.driveForward|param|motors": "@param speed the desired speed, eg: 50"
}

View File

@ -0,0 +1,7 @@
{
"behaviors.addBehavior|block": "add behavior %behavior",
"behaviors.avoidCrash|block": "avoid crash using %ultrasonic",
"behaviors.driveForward|block": "drive %motors|forward at %speed|%",
"behaviors|block": "behaviors",
"{id:category}Behaviors": "Behaviors"
}

6
libs/behaviors/pxt.json Normal file
View File

@ -0,0 +1,6 @@
{
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/behaviors",
"dependencies": {
"core": "file:../ev3"
}
}

View File

@ -0,0 +1,56 @@
namespace behaviors {
class AvoidCrashBehavior extends behaviors.Behavior {
private ultrasonic: sensors.UltraSonicSensor;
constructor(ultrasonic: sensors.UltraSonicSensor) {
super();
this.ultrasonic = ultrasonic;
}
shouldRun(): boolean {
return this.ultrasonic.distance() < 5;
}
run(): void {
motors.stopAllMotors();
this.active = false;
}
}
/**
* A behavior that stops all motors if the sensor distance get too short
*/
//% blockId=behaviorsAvoidCrash block="avoid crash using %ultrasonic"
export function avoidCrash(ultrasonic: sensors.UltraSonicSensor) : behaviors.Behavior {
return new AvoidCrashBehavior(ultrasonic);
}
class DriveForwardBehavior extends behaviors.Behavior {
private motors: motors.MotorBase;
private speed: number;
constructor(motors: motors.MotorBase, speed: number) {
super();
this.motors = motors;
this.speed = speed;
}
shouldRun(): boolean {
return true;
}
run(): void {
this.motors.setSpeed(this.speed);
pauseUntil(() => !this.active);
this.motors.setSpeed(0);
}
}
/**
* A behavior that turns on the motors to the specified speed
* @param motors
* @param speed the desired speed, eg: 50
*/
//% blockId=behaviorsDriveForward block="drive %motors|forward at %speed|%"
export function driveForward(motors: motors.MotorBase, speed: number): behaviors.Behavior {
return new DriveForwardBehavior(motors, speed);
}
}

View File

@ -111,6 +111,7 @@ namespace sensors {
//% blockId=colorOnColorDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=100 blockGap=8
//% group="Color Sensor"
onColorDetected(color: ColorSensorColor, handler: () => void) {
@ -130,6 +131,7 @@ namespace sensors {
//% blockId=colorPauseForColorDetected
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99 blockGap=8
//% group="Color Sensor"
pauseForColor(color: ColorSensorColor) {
@ -149,6 +151,7 @@ namespace sensors {
//% blockId=colorGetColor
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99
//% group="Color Sensor"
color(): ColorSensorColor {
@ -166,6 +169,7 @@ namespace sensors {
//% blockId=colorOnLightChanged
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=89 blockGap=8
//% group="Color Sensor"
onLightChanged(mode: LightIntensityMode, condition: LightCondition, handler: () => void) {
@ -182,6 +186,7 @@ namespace sensors {
//% blockId=colorPauseForLight
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=88 blockGap=8
//% group="Color Sensor"
pauseForLight(mode: LightIntensityMode, condition: LightCondition) {
@ -199,6 +204,7 @@ namespace sensors {
//% blockId=colorLight
//% parts="colorsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=87
//% group="Color Sensor"
light(mode: LightIntensityMode) {

View File

@ -52,9 +52,10 @@
"control.raiseEvent": "Announce that an event happened to registered handlers.",
"control.raiseEvent|param|src": "ID of the Component that generated the event",
"control.raiseEvent|param|value": "Component specific code indicating the cause of the event.",
"motors.Motor.angle": "Gets motor ration angle.",
"motors.Motor.clearCount": "Clears the motor count",
"motors.Motor.angle": "Gets motor angle.",
"motors.Motor.clearCounts": "Clears the motor count",
"motors.Motor.speed": "Gets motor actual speed.",
"motors.Motor.tacho": "Gets motor tachometer count.",
"motors.Motor.toString": "Returns the status of the motor",
"motors.MotorBase.isReady": "Returns a value indicating if the motor is still running a previous command.",
"motors.MotorBase.move": "Moves the motor by a number of rotations, degress or seconds",

View File

@ -47,7 +47,9 @@
"control.raiseEvent|block": "raise event|from %src|with value %value",
"control|block": "control",
"motors.Motor.angle|block": "%motor|angle",
"motors.Motor.clearCounts|block": "%motor|clear counts",
"motors.Motor.speed|block": "%motor|speed",
"motors.Motor.tacho|block": "%motor|tacho",
"motors.MotorBase.move|block": "move %motor|for %value|%unit|at %speed|%",
"motors.MotorBase.pauseUntilReady|block": "%motor|pause until ready",
"motors.MotorBase.setBrake|block": "set %motor|brake %brake",

View File

@ -109,7 +109,7 @@ namespace motors {
* Stops all motors
*/
//% blockId=motorStopAll block="stop all motors"
//% weight=97
//% weight=5
//% group="Motion"
export function stopAllMotors() {
const b = mkCmd(Output.ALL, DAL.opOutputStop, 0)
@ -258,7 +258,7 @@ namespace motors {
/**
* Returns a value indicating if the motor is still running a previous command.
*/
//%
//% group="Sensors"
isReady(): boolean {
this.init();
const buf = mkCmd(this._port, DAL.opOutputTest, 2);
@ -278,6 +278,7 @@ namespace motors {
* @param timeOut optional maximum pausing time in milliseconds
*/
//% blockId=motorPauseUntilRead block="%motor|pause until ready"
//% weight=97
//% group="Motion"
pauseUntilReady(timeOut?: number) {
pauseUntil(() => this.isReady(), timeOut);
@ -330,7 +331,8 @@ namespace motors {
* @param motor the port which connects to the motor
*/
//% blockId=motorSpeed block="%motor|speed"
//% weight=72 blockGap=8
//% weight=72
//% blockGap=8
//% group="Sensors"
speed(): number {
this.init();
@ -338,10 +340,10 @@ namespace motors {
}
/**
* Gets motor ration angle.
* Gets motor angle.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="%motor|angle"
//% blockId=motorAngle block="%motor|angle"
//% weight=70
//% group="Sensors"
angle(): number {
@ -349,11 +351,28 @@ namespace motors {
return getMotorData(this._port).count;
}
/**
* Gets motor tachometer count.
* @param motor the port which connects to the motor
*/
//% blockId=motorTachoCount block="%motor|tacho"
//% weight=69
//% blockGap=8
//% group="Sensors"
tacho(): number {
this.init();
return getMotorData(this._port).tachoCount;
}
/**
* Clears the motor count
*/
//% group="Motion"
clearCount() {
//% blockId=motorClearCount block="%motor|clear counts"
//% weight=68
//% blockGap=8
//% group="Sensors"
clearCounts() {
this.init();
const b = mkCmd(this._port, DAL.opOutputClearCount, 0)
writePWM(b)
@ -426,7 +445,7 @@ namespace motors {
private __setSpeed(speed: number) {
syncMotors(this._port, {
speed: speed,
turnRatio: 0,
turnRatio: 100, // same speed
useBrake: !!this._brake
})
}

View File

@ -151,9 +151,9 @@ namespace brick {
* @param image the image
*/
//% blockId=screen_image_picker block="%image" shim=TD_ID
//% image.fieldEditor="imagedropdown"
//% image.fieldEditor="images"
//% image.fieldOptions.columns=6
//% image.fieldOptions.hasSearchBar=true
//% image.fieldOptions.width=600
//% group="Screen" weight=0 blockHidden=1
export function __imagePicker(image: Image): Image {
return image;

View File

@ -19,6 +19,10 @@ namespace sensors {
namespace motors {
}
//% labelLineWidth=0
namespace behaviors {
}
//% color="#D67923" weight=80 icon="\uf10e"
namespace music {

View File

@ -28,6 +28,7 @@ namespace sensors {
//% blockId=gyroGetAngle
//% parts="gyroscope"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=65 blockGap=8
//% group="Gyro Sensor"
angle(): number {
@ -44,6 +45,7 @@ namespace sensors {
//% blockId=gyroGetRate
//% parts="gyroscope"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=65 blockGap=8
//% group="Gyro Sensor"
rate(): number {

View File

@ -266,8 +266,10 @@ namespace music {
//% blockId=music_play_sound_effect_until_done block="play sound effect %sound|until done"
//% weight=98 blockGap=8
export function playSoundEffectUntilDone(sound: Sound) {
if (!sound) return;
if (!sound || numSoundsPlaying >= soundsLimit) return;
numSoundsPlaying++;
sound.play();
numSoundsPlaying--;
}
/**

View File

@ -1,9 +1,11 @@
{
"tests": "Unit tests framework",
"TestEvent": "Various test event in the execution cycle",
"tests": "A Unit tests framework",
"tests.assert": "Checks a boolean condition",
"tests.assertClose": "Checks that 2 values are close to each other",
"tests.assertClose|param|actual": "what the value was",
"tests.assertClose|param|expected": "what the value should be",
"tests.assertClose|param|tolerance": "the acceptable error margin",
"tests.assertClose|param|tolerance": "the acceptable error margin, eg: 5",
"tests.onEvent": "Registers code to be called at various points in the test execution",
"tests.test": "Registers a test to run"
}

View File

@ -1,4 +1,9 @@
{
"TestEvent.RunSetUp|block": "run setup",
"TestEvent.RunTearDown|block": "run teardown",
"TestEvent.TestSetUp|block": "test setup",
"TestEvent.TestTearDown|block": "test teardown",
"tests.assertClose|block": "assert %message|%expected|close to %actual|by %tolerance",
"tests.assert|block": "assert %message|%condition",
"tests.test|block": "test %name",
"tests|block": "tests",

View File

@ -1,14 +1,6 @@
{
"name": "tests",
"description": "A unit test library",
"files": [
"README.md",
"tests.ts"
],
"testFiles": [
],
"public": true,
"additionalFilePath": "../../node_modules/pxt-common-packages/libs/tests",
"dependencies": {
"core": "file:../core"
"core": "file:../ev3"
}
}

View File

@ -0,0 +1,12 @@
// EV3 specific test functions
tests.onEvent(TestEvent.RunSetUp, function() {
console.sendToScreen();
})
tests.onEvent(TestEvent.TestSetUp, function() {
motors.stopAllMotors();
motors.resetAllMotors();
})
tests.onEvent(TestEvent.TestTearDown, function() {
motors.stopAllMotors();
motors.resetAllMotors();
})

View File

@ -1,94 +0,0 @@
/**
* Unit tests framework
*/
//% weight=100 color=#0fbc11 icon=""
namespace tests {
class Test {
name: string;
handler: () => void;
errors: string[];
constructor(name: string, handler: () => void) {
this.name = name;
this.handler = handler;
this.errors = [];
}
reset() {
motors.stopAllMotors();
motors.resetAllMotors();
}
run() {
// clear state
this.reset();
console.log(`> ${this.name}`)
this.handler()
if (this.errors.length)
console.log('')
// ensure clean state after test
this.reset();
}
}
let _tests: Test[] = undefined;
let _currentTest: Test = undefined;
function run() {
if (!_tests) return;
const start = control.millis();
console.sendToScreen();
console.log(`${_tests.length} tests found`)
console.log(` `)
for (let i = 0; i < _tests.length; ++i) {
const t = _currentTest = _tests[i];
t.run();
_currentTest = undefined;
}
console.log(` `)
console.log(`${_tests.length} tests, ${_tests.map(t => t.errors.length).reduce((p, c) => p + c, 0)} errs in ${Math.ceil((control.millis() - start) / 1000)}s`)
}
/**
* Registers a test to run
*/
//% blockId=testtest block="test %name"
export function test(name: string, handler: () => void): void {
if (!name || !handler) return;
if (!_tests) {
_tests = [];
control.runInBackground(function () {
// should run after on start
loops.pause(100)
run()
})
}
_tests.push(new Test(name, handler));
}
/**
* Checks a boolean condition
*/
//% blockId=testAssert block="assert %message|%condition"
export function assert(message: string, condition: boolean) {
if (!condition) {
console.log(`!!! ${message || ''}`)
if (_currentTest)
_currentTest.errors.push(message);
}
}
/**
* Checks that 2 values are close to each other
* @param expected what the value should be
* @param actual what the value was
* @param tolerance the acceptable error margin
*/
export function assertClose(name: string, expected: number, actual: number, tolerance: number) {
assert(`${name} ${expected} != ${actual} +-${tolerance}`, Math.abs(expected - actual) <= tolerance);
}
}

View File

@ -45,6 +45,7 @@ namespace sensors {
//% blockId=touchEvent block="on %sensor|%event"
//% parts="touch"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99 blockGap=8
//% group="Touch Sensor"
onEvent(ev: TouchSensorEvent, body: () => void) {
@ -60,6 +61,7 @@ namespace sensors {
//% blockId=touchWaitUntil block="pause until %sensor|%event"
//% parts="touch"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=98 blockGap=8
//% group="Touch Sensor"
pauseUntil(ev: TouchSensorEvent) {
@ -75,6 +77,7 @@ namespace sensors {
//% blockId=touchIsPressed
//% parts="touch"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=81
//% group="Touch Sensor"
isPressed() {
@ -90,6 +93,7 @@ namespace sensors {
//% blockId=touchWasPressed
//% parts="touch"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=81 blockGap=8
//% group="Touch Sensor"
wasPressed() {

View File

@ -46,6 +46,7 @@ namespace sensors {
//% block="on %sensor|%event"
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=100 blockGap=8
//% group="Ultrasonic Sensor"
onEvent(event: UltrasonicSensorEvent, handler: () => void) {
@ -60,6 +61,7 @@ namespace sensors {
//% blockId=ultrasonicWait
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=99 blockGap=8
//% group="Ultrasonic Sensor"
pauseUntil(event: UltrasonicSensorEvent) {
@ -75,6 +77,7 @@ namespace sensors {
//% blockId=sonarGetDistance
//% parts="ultrasonicsensor"
//% blockNamespace=sensors
//% sensor.fieldEditor="ports"
//% weight=65
//% group="Ultrasonic Sensor"
distance(): number {

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.49",
"version": "0.0.51",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "pxt-ev3",
"version": "0.0.49",
"version": "0.0.52",
"description": "LEGO Mindstorms EV3 for Microsoft MakeCode",
"private": true,
"keywords": [
@ -44,7 +44,7 @@
"webfonts-generator": "^0.4.0"
},
"dependencies": {
"pxt-common-packages": "0.14.13",
"pxt-common-packages": "0.15.3",
"pxt-core": "3.0.5"
},
"scripts": {

View File

@ -16,7 +16,8 @@
"libs/infrared-sensor",
"libs/gyro-sensor",
"libs/ev3",
"libs/tests"
"libs/tests",
"libs/behaviors"
],
"simulator": {
"autoRun": true,
@ -94,6 +95,7 @@
"privacyUrl": "https://go.microsoft.com/fwlink/?LinkId=521839",
"termsOfUseUrl": "https://go.microsoft.com/fwlink/?LinkID=206977",
"githubUrl": "https://github.com/Microsoft/pxt-ev3",
"betaUrl": "https://lego.makecode.com/",
"boardName": "LEGO Mindstorms EV3 Brick",
"selectLanguage": true,
"highContrast": true,

View File

@ -10,8 +10,6 @@ namespace pxsim.music {
}
namespace pxsim.SoundMethods {
let numSoundsPlaying = 0;
const soundsLimit = 1;
let audio: HTMLAudioElement;
export function buffer(buf: RefBuffer) {
@ -27,18 +25,15 @@ namespace pxsim.SoundMethods {
}
export function play(buf: RefBuffer, volume: number) {
if (!buf || numSoundsPlaying >= soundsLimit) {
if (!buf) {
return Promise.resolve();
}
return new Promise<void>(resolve => {
let url = "data:audio/wav;base64," + btoa(uint8ArrayToString(buf.data))
audio = new Audio(url)
audio.onended = () => {
resolve();
numSoundsPlaying--;
}
numSoundsPlaying++;
audio.play()
audio.onended = () => resolve();
audio.onpause = () => resolve();
audio.play();
})
}
@ -46,8 +41,8 @@ namespace pxsim.SoundMethods {
return new Promise<void>(resolve => {
if (audio) {
audio.pause();
numSoundsPlaying--;
}
resolve();
})
}

View File

@ -4,12 +4,7 @@
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:none;stroke:#717171;}
</style>
<title>color</title>
<g id="menu_icn_expansions" transform="translate(-9596 11554)">
<circle id="Ellipse_34" class="st0" cx="9607.5" cy="-11542" r="9"/>
<polygon class="st1" points="9613.5,-11543 9608.5,-11543 9608.5,-11548 9606.5,-11548 9606.5,-11543 9601.5,-11543 9601.5,-11541
9606.5,-11541 9606.5,-11536 9608.5,-11536 9608.5,-11541 9613.5,-11541 "/>
</g>
<path class="st0" d="M11.5,2.5c-5,0-9,4-9,9s4,9,9,9s9-4,9-9S16.5,2.5,11.5,2.5z M17.5,12.5h-5v5h-2v-5h-5v-2h5v-5h2v5h5V12.5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 802 B

After

Width:  |  Height:  |  Size: 552 B

View File

@ -19,12 +19,13 @@ webfontsGenerator({
"./categories/sensors.svg",
"./categories/text.svg",
"./categories/variables.svg",
"./categories/advancedcollapsed.svg",
"./categories/advancedexpanded.svg",
"./icons/cancel.svg",
"./icons/check.svg",
"./icons/download.svg",
"./icons/save.svg",
"./categories/advancedcollapsed.svg",
"./categories/advancedexpanded.svg"
"./icons/blocks.svg"
],
dest: '../docs/static/fonts/icons/',
round: 10

14
svgicons/icons/blocks.svg Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="svg41" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<g id="menu_icn_blocks" transform="translate(-9594.255 11170)">
<rect id="Rectangle_271" x="9600.5" y="-11160.2" class="st0" width="16.2" height="3.5"/>
<polygon class="st0" points="9616.8,-11162.6 9616.8,-11166 9594.8,-11166 9594.8,-11164.9 9594.8,-11162.6 9594.8,-11154.4
9594.8,-11151 9616.8,-11151 9616.8,-11154.4 9598.2,-11154.4 9598.2,-11162.6 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -6,10 +6,6 @@
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<g id="icn_cancel_x" transform="translate(14923.365 9661)">
<rect id="Rectangle_277" x="-14920.9" y="-9651.1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2455.6738 -13370.6289)" class="st0" width="18" height="3"/>
<rect id="Rectangle_278" x="-14913.4" y="-9658.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2455.6738 -13370.6299)" class="st0" width="3" height="18"/>
</g>
<polygon class="st0" points="19,6 16.9,3.9 11.6,9.2 6.3,3.9 4.2,6 9.5,11.3 4.2,16.6 6.3,18.8 11.6,13.5 16.9,18.8 19,16.6
13.7,11.3 "/>
</svg>

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 562 B

View File

@ -6,10 +6,5 @@
.st0{fill:#FFFFFF;}
</style>
<title>color</title>
<g id="icn_cancel_ok" transform="translate(15066.061 9654)">
<rect id="Rectangle_275" x="-15061" y="-9644.1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2409.7021 -13467.6172)" class="st0" width="18" height="3"/>
<rect id="Rectangle_276" x="-15061.3" y="-9643.9" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2405.1626 -13472.2129)" class="st0" width="3" height="9"/>
</g>
<polygon class="st0" points="19.4,4.1 8.8,14.7 4.3,10.2 2.2,12.4 6.7,16.8 6.7,16.8 8.8,18.9 21.5,6.2 "/>
</svg>

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 530 B

View File

@ -4,22 +4,15 @@
viewBox="0 0 23 23" style="enable-background:new 0 0 23 23;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:none;}
.st2{fill:none;stroke:#FFFFFF;}
</style>
<title>color</title>
<g id="menu_icn_savedisc" transform="translate(-8886 12201.056)">
<path id="Path_116" class="st0" d="M8887.3-12189.6v-9.5c0-0.7,0.1-0.8,0.8-0.8h4.5c0.7,0,0.8,0.1,0.8,0.8v4.6
c0,0.6,0.1,0.7,0.7,0.7h6.8c0.6,0,0.7-0.1,0.7-0.7v-4.7c0-0.6,0.1-0.7,0.7-0.7c1.4,0,1.4,0,2.3,0.9c1,1,1.9,1.9,2.9,2.9
c0.2,0.2,0.3,0.5,0.3,0.8v15.2c0,0.6-0.1,0.8-0.7,0.8H8888c-0.6,0-0.7-0.1-0.7-0.8C8887.3-12183.3,8887.3-12186.5,8887.3-12189.6z
M8897.6-12181.5h7.4c0.7,0,0.8-0.1,0.8-0.8v-8.7c0-0.6-0.1-0.8-0.8-0.8h-14.9c-0.6,0-0.8,0.1-0.8,0.8v8.7c0,0.7,0.1,0.8,0.8,0.8
H8897.6z"/>
<path id="Path_117" class="st0" d="M8900.2-12197.9c0,0.5,0,1,0,1.5c0,0.5-0.2,0.6-0.6,0.6c-0.3,0-0.7,0-0.9,0s-0.6-0.1-0.6-0.5
v-0.1v-2.9c0-0.2,0.1-0.5,0.5-0.6c0,0,0,0,0.1,0c0.3,0,0.7,0,1,0c0.3,0,0.6,0.2,0.6,0.5c0,0,0,0,0,0.1
C8900.2-12198.9,8900.2-12198.3,8900.2-12197.9z"/>
<g id="Rectangle_166" transform="translate(1.445 6.558)">
<rect x="8887.5" y="-12199" class="st1" width="17.4" height="11.6"/>
<rect x="8888.1" y="-12198.5" class="st2" width="16.3" height="10.5"/>
</g>
<path id="Path_117" class="st0" d="M8900.4-12197.9c0,0.5,0,1,0,1.5c0,0.5-0.2,0.6-0.6,0.6c-0.3,0-0.7,0-0.9,0
c-0.2,0-0.6-0.1-0.6-0.5v-0.1v-2.9c0-0.2,0.1-0.5,0.5-0.6c0,0,0,0,0.1,0c0.3,0,0.7,0,1,0c0.3,0,0.6,0.2,0.6,0.5c0,0,0,0,0,0.1
C8900.4-12198.9,8900.4-12198.3,8900.4-12197.9z"/>
<path class="st0" d="M8907.6-12196c-1-1-1.9-1.9-2.9-2.9c-0.9-0.9-0.9-0.9-2.3-0.9c-0.6,0-0.7,0.1-0.7,0.7v4.7
c0,0.6-0.1,0.7-0.7,0.7h-6.8c-0.6,0-0.7-0.1-0.7-0.7v-4.6c0-0.7-0.1-0.8-0.8-0.8h-4.5c-0.7,0-0.8,0.1-0.8,0.8v9.5
c0,3.2,0,6.3,0,9.6c0,0.7,0.1,0.8,0.7,0.8h19.1c0.6,0,0.7-0.2,0.7-0.8v-15.2C8908-12195.6,8907.8-12195.8,8907.6-12196z
M8905.4-12181.9h-15.3v-9.4h15.3V-12181.9z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -9,7 +9,7 @@
/* Fonts for toolbox icons */
@font-face {
font-family: 'legoIcons';
src: data-uri("../docs/static/fonts/icons/iconfont.woff2");
src: data-uri("../docs/static/fonts/icons/iconfont.woff");
}
.blocklyFlyoutLabel:not(.blocklyFlyoutHeading) .blocklyFlyoutLabelIcon {
@ -118,34 +118,34 @@ span.blocklyTreeIcon.blocklyTreeIconaddpackage::before {
}
span.blocklyTreeIcon.blocklyTreeIconadvancedcollapsed::before {
content: "\f116";
}
span.blocklyTreeIcon.blocklyTreeIconadvancedexpanded::before {
content: "\f117";
}
.save-editortools-btn .icon.save,
.download-button .icon.download.icon-and-text,
.button.approve.positive .i.icon.checkmark,
.button.approve.cancel .i.icon.cancel
{
font-family: 'legoIcons';
}
.save-editortools-btn .icon.save:before {
content: "\f115";
}
.download-button .icon.download.icon-and-text:before {
content: "\f114";
}
.button.approve.positive .i.icon.checkmark:before {
content: "\f113";
}
.button.approve.cancel .i.icon.cancel:before {
content: "\f112";
}
span.blocklyTreeIcon.blocklyTreeIconadvancedexpanded::before {
content: "\f113";
}
.save-editortools-btn .icon.save:before {
font-family: 'legoIcons';
content: "\f117";
}
.download-button .icon.download.icon-and-text:before {
font-family: 'legoIcons';
content: "\f116";
}
.button.approve.positive i.icon.checkmark:before {
font-family: 'legoIcons';
content: "\f115";
}
.button.approve.cancel i.icon.cancel:before {
font-family: 'legoIcons';
content: "\f114";
}
.blocks-menuitem i.icon.blocks:before {
font-family: 'legoIcons' !important;
content: "\f118" !important;
}

View File

@ -76,6 +76,12 @@
color:white !important;
}
/* lowercase menu items */
.menubar .ui.menu.fixed > .left.menu > .ui.item > .ui.text,
.menubar .ui.menu.fixed > .ui.item > span {
text-transform: lowercase;
}
/* Mobile */
@media only screen and (max-width: @largestMobileScreen) {
#filelist {