Microsoft MakeCode

Making my own block that has a body and is not a top block

I am trying to make a block that groups other blocks. In my case, these groups form a group of shapes on a 3D surface.

The repeat block shape is perfect for this. And with some difficulty, I managed to convert the forever block from the pxt-sample into the shape of a repeat block.

subtractshapes

//% blockId=subtract_shapes block="subtract shapes" 
//% topblock=false
//% handlerStatement=true
export function subtractShapes(body: RefAction): void {
    board().addBlock("difference( <CHILDREN> )"); // add a JSCad statement to the interpreter.

    thread.runInBackground(body)

}

//% block="cylinder radius $radius|height $height"
//% inlineInputMode=inline
//% radius.defl=10
//% height.defl=10
export function cylinder(radius: number, height: number) {
    board().addStatement(`cylinder({r1: ${radius}, r2: ${radius}, h: ${height}})`);
}

My question is I have no idea what to do with the body. I have run thread.runInBackground but I don’t think that is correct, because I need the statements to be added deterministically. This code works fine but when I add in a repeat loop it all goes to crazytown. What should I do to make sure that the code for cylinder and cube gets called?

You should be able to “call” body:

export function subtractShapes(body: RefAction): void {
   board().addBlock("difference( <CHILDREN> )"); // add a JSCad statement to the interpreter.
   body()
}

Neat! Will give that a go.

So close, but no typescript love.

Thoughts?
bodyhasnocallsig

you need to use runFiberAsync(). Like this:

//% blockId=subtract_shapes block="subtract shapes" 
//% topblock=false
//% handlerStatement=true
//% promise
export function subtractShapesAsync(body: RefAction): Promise<void> {
    board().addBlock("difference( <CHILDREN> )"); // add a JSCad statement to the interpreter.

    return pxsim.runtime.runFiberAsync(body)

}

Note that I also added the promise annotation and changed the name to end in Async. Makecode will emit a declaration that looks like this to the user:

export function subtractShapes(body: () => void): void

So they won’t see anything async related :slight_smile:

1 Like

Also, I should mention, generally it’s best to do APIs that take in non-primitives inside of libs/ instead of in the simulator. Then you don’t have to deal with internals directly like this. For example, you could have an API in the simulator like this:

//%
export function addBlock(block: string) {
    board().addBlock(block)
}

and then define the public function in the libs folder like this:

//% blockId=subtract_shapes block="subtract shapes" 
//% topblock=false
//% handlerStatement=true
export function subtractShapes(body: () => void): void {
    internalNamespace.addBlock("difference( <CHILDREN> )")
    body()
}

If you start the name of the internal function with an underscore (e.g. _addBlock) then it won’t show up to the user in completions.

1 Like

Can I ask which file you would pop it into in /libs… does it matter? This bit seems to be missing from the documentation… I started from the pxt-sample project… so I am assuming it’s ns.ts cause that’s got Turtle stuff in it?

where

You can think of all the TypeScript code in libs as “user code”, the files in there don’t have any special importance; any TS file that is listed in pxt.json will be added to the user’s project as a dependency. If you look at the pxt.json inside of the blocksprj project, you’ll see it has a dependency on the “core” package that contains ns.ts.

In general, writing as much code as possible in libs is helpful because it makes the code more portable between editors. It also lets you use the MakeCode debugger with your code.

BTW, the name ns.ts is for “namespace” because that’s the file that defines the namespaces and their colors/names in the UI.

1 Like

Legend! Thanks this helps a lot. I will give it all a go.
JFo