Make Go To Layer Command Non-Cyclic

Hi all,

When I hold down the up arrow key to move up through the layer stack, after reaching the top-most layer I cycle to the bottom. When I hold the down arrow key, after reaching the bottom-most layer I cycle to the top. My feature request is for this not to cycle… or for it to be configurable.

I realize that there may be just as many who prefer the cycling. So even though I’m posting this topic under Features, I found it was worth the time to hack in a change myself – it’s a line swap. If you have the option to customize and build Aseprite yourself, the relevant file is cmd_goto_layer.cpp:

if (m_offset > 0) {
    int i = m_offset;
    while (i-- > 0) {
        layer = layer->getNextBrowsable();
        if (!layer) {
            // layer = site.sprite()->firstBrowsableLayer();
            layer = site.sprite()->root()->lastLayer();
        }
    }
} else if (m_offset < 0) {
    int i = m_offset;
    while (i++ < 0) {
        layer = layer->getPreviousBrowsable();
        if (!layer) {
            // layer = site.sprite()->root()->lastLayer();
            layer = site.sprite()->firstBrowsableLayer();
        }
    }
}

In my opinion, anything related to layer hierarchies (and therefore requiring while loops or recursive methods) is a pain to script in Lua, even when technically possible. Here’s a draft for a function to ascend the layer stack with the up arrow key:

local ignoreCollapsed = false
local activeSprite = app.activeSprite
if activeSprite then
    local activeLayer = app.activeLayer
    if activeLayer then
        -- Parent could be sprite or group layer.
        local activeParent = activeLayer.parent
        local activeStackIndex = activeLayer.stackIndex
        local parentLayers = activeParent.layers
        local parentLayersLen = #parentLayers
        if activeStackIndex < parentLayersLen then
            local nextStackIndex = activeStackIndex + 1
            local nextLayer = parentLayers[nextStackIndex]
            while nextLayer.isGroup
                and #nextLayer.layers > 0
                and (ignoreCollapsed
                  or nextLayer.isExpanded) do
                nextLayer = nextLayer.layers[1]
            end
            app.activeLayer = nextLayer
        elseif activeParent.__name == "doc::Sprite" then
            app.activeLayer = activeSprite.layers[#activeSprite.layers]
        else
            app.activeLayer = activeParent
        end
    else
        app.activeLayer = activeSprite.layers[#activeSprite.layers]
    end
end

Maybe open Task Manager (or its equivalent on your OS) first before you try it, just in case. :stuck_out_tongue:

Afaik Layer.parent doesn’t return nil. Instead, it returns either a sprite or a layer – both userdata as far as Lua’s type is concerned. The hack that I know of is to check the __name for a string match.

print(type(app.activeSprite))
print(type(app.activeLayer))
print(app.activeSprite.__name)
print(app.activeLayer.__name)

One advantage to the Lua approach that surprised me: You can customize whether to ignore the collapsed / expanded status of the next layer. It gives you the equivalent distinction of ‘step over’ vs. ‘step into’. Luckily, Aseprite’s UI seems to automatically expand a parent layer if you step into one of its children.

I’m trusting that anyone who’s actually interested in the Lua approach can figure out how to create the script to descend the stack (maybe even a better way to do it).

Cheers,
Jeremy