More types for blockify option

Sometimes, when you look at a list/table monitor and right click it, there will be a blockify… option you can then press to generate a list reporter block with the contents of the list. However, if your list has a color or ring somewhere in it, the option wont appear at all. Looking through the code I can see this happens blockify… is only available when the list could be JSON (e.g. only text, numbers, objects, lists, and booleans) instead of also color and rings.

And actually, I just tried to code it in and I did it succesfully!

Might be advanced

My solution was to add a seperate canBeBlockified function to the List class that also allows colors and contexts, then I simply added those in the blockify function. Next, I updated the TableMorph and ListWatcherMorph to instead use this new function. Heres my code for that:

List.prototype.canBeBlockified = function (already = []) {
    return this.itemsArray().every(value => !isNaN(+value) ||
        isString(value) ||
        value === true ||
        value === false ||
        value instanceof Color ||
        value instanceof Context ||
        (value instanceof List &&
            !already.includes(value) &&
            value.canBeBlockified(already.concat([value]))) // detect circularity
    );
};

List.prototype.blockify = function (limit = 500, count = [0]) {
    var block = SpriteMorph.prototype.blockForSelector('reportNewList'),
        slots = block.inputs()[0],
        len = this.length(),
        itemBlock,
        i, value;

    block.isDraggable = true;
    slots.removeInput();

    // fill the slots with the data
    for (i = 0; i < len && count[0] < limit; i += 1) {
        value = this.at(i + 1);
        if (value instanceof List) {
            slots.replaceInput(
                slots.addInput(),
                value.blockify(limit, count)
            );
        } else if (typeof value === 'boolean') {
            itemBlock = SpriteMorph.prototype.blockForSelector('reportBoolean');
            itemBlock.inputs()[0].setContents(value);
            itemBlock.isDraggable = true;
            slots.replaceInput(
                slots.addInput(),
                itemBlock
            );
        } else if (value instanceof Color) {
            itemBlock = SpriteMorph.prototype.blockForSelector('reportColor');
            itemBlock.inputs()[0].setContents(value);
            itemBlock.isDraggable = true;
            slots.replaceInput(
                slots.addInput(),
                itemBlock
            );
        } else if (value instanceof Context) {
            itemBlock = value.toBlock();
            itemBlock.isDraggable = true;
            slots.replaceInput(
                slots.addInput(),
                itemBlock
            );
        } else {
            slots.addInput(value);
        }
        count[0] += 1;
    }

    slots.fixBlockColor(null, true);
    return block;
};

ListWatcherMorph.prototype.userMenu = function () {
    var world = this.world(),
        ide = detect(world.children, m => m instanceof IDE_Morph);

    if (!List.prototype.enableTables || ide.isAppMode) {
        return this.escalateEvent('userMenu');
    }
    var menu = new MenuMorph(this);
    menu.addItem('table view...', 'showTableView');
    if (this.list.canBeBlockified()) {
        menu.addItem(
            'blockify',
            () => {
                this.list.blockify().pickUp(world);
                world.hand.grabOrigin = {
                    origin: ide.palette,
                    position: ide.palette.center()
                };
            }
        );
    }
    if (this.list.canBeJSON()) {
        menu.addItem(
            'export',
            () => {
                if (this.list.canBeCSV()) {
                    ide.saveFileAs(
                        this.list.asCSV(),
                        'text/csv;charset=utf-8', // RFC 4180
                        localize('data') // name
                    );
                } else {
                    ide.saveFileAs(
                        this.list.asJSON(),
                        'text/json;charset=utf-8',
                        localize('data') // name
                    );
                }
            }
        );
    }
    menu.addLine();
    menu.addItem(
        'open in dialog...',
        () => new TableDialogMorph(this.list).popUp(this.world())
    );
    return menu;
};

TableMorph.prototype.userMenu = function () {
    var menu = new MenuMorph(this),
        world = this.world(),
        ide = detect(world.children, m => m instanceof IDE_Morph);

    if (ide.isAppMode) {return; }
    if (this.parentThatIsA(TableDialogMorph)) {
        if (this.colWidths.length) {
            menu.addItem('reset columns', 'resetColumns');
            menu.addLine();
        }
        if (this.table instanceof List && this.table.canBeJSON()) {
            menu.addItem(
                'blockify',
                () => {
                    this.table.blockify().pickUp(world);
                    world.hand.grabOrigin = {
                        origin: ide.palette,
                        position: ide.palette.center()
                    };
                }
            );
            menu.addItem(
                'export',
                () => {
                    if (this.table.canBeCSV()) {
                        ide.saveFileAs(
                            this.table.asCSV(),
                            'text/csv;charset=utf-8', // RFC 4180
                            localize('data') // name
                        );
                    } else {
                        ide.saveFileAs(
                            this.table.asJSON(true), // guessObjects
                            'text/json;charset=utf-8',
                            localize('data') // name
                        );
                    }
                }
            );
        }
        menu.addItem('open in another dialog...', 'openInDialog');
        return menu;
    }

    if (this.colWidths.length) {
        menu.addItem('reset columns', 'resetColumns');
    }
    menu.addItem('list view...', 'showListView');
    if (this.table instanceof List) {
        if (this.table.canBeBlockified()) {
            menu.addItem(
                'blockify',
                () => {
                    this.table.blockify().pickUp(world);
                    world.hand.grabOrigin = {
                        origin: ide.palette,
                        position: ide.palette.center()
                    };
                }
            );
        }
        
        if (this.table.canBeJSON()) {
            menu.addItem(
                'export',
                () => {
                    if (this.table.canBeCSV()) {
                        ide.saveFileAs(
                            this.table.asCSV(),
                            'text/csv;charset=utf-8', // RFC 4180
                            localize('data') // name
                        );
                    } else {
                        ide.saveFileAs(
                            this.table.asJSON(true), // guessObjects
                            'text/json;charset=utf-8',
                            localize('data') // name
                        );
                    }
                }
            );
        }
        
    }
    menu.addLine();
    menu.addItem('open in dialog...', 'openInDialog');
    return menu;
};

Hope this gets added in Snap! 12!

It is a slight nuisance (partly because I keep forgetting the unused blocks option and delete all library blocks manually), but what I do is just import the metaprogramming library and use the blockify block. It has a very simple and satisfying definition:

Edit: Just realized the library block doesn’t work with colours. I’ve fixed it:

Yeah, that does exist, but it would be much easier to have that in normal Snap!.

What about agents (sprites and stage) and media (costumes and sounds)?

The only way for costumes and sounds to be iin a block is creating them from data, but for agents, if they are in the corral you could use a object [ v] block to represent them.

is there any representation you think would work?
I’m honestly quite surprised adding an image representation of a costume in an input hasn’t been discussed more.

I think there was a Scratch 1.4 mod that had something like that for a pen block.


(mockup)
(wait why did I reply to an old topic WHY?!)

Its only from 26 days ago.

But yeah that block would really be useful…

base64@MQTT library can handle sound,costume serialization


=>


But… the “blockify” returns a stack of blocks, i.e., script. What’s the point in encoding such a vast amount of data (Alonzo 70kB) as program “code”? It can’t be displayed nor manipulated. Smart pic seems to not work either.
With the :snap: persistent variables it should be enough to
blockify script pic (10)

Can you describe the intended use case?