Rename Multiple Layers With Index Number

Below is my take on a layer rename dialog.

---@param layer Layer
---@param tally integer
---@param idLayerDict table<integer, integer>
---@return integer
local function layerHierarchy(layer, tally, idLayerDict)
    if layer.isGroup then
        local children <const> = layer.layers
        if children then
            local lenChildren <const> = #children
            local i = 0
            while i < lenChildren do
                i = i + 1
                tally = layerHierarchy(children[i], tally, idLayerDict)
            end
        end
    end

    idLayerDict[layer.id] = tally + 1
    return tally + 1
end

---@param sprite Sprite
---@return table<integer, integer>
local function spriteHierarchy(sprite)
    ---@type table<integer, integer>
    local idLayerDict <const> = {}
    local tally = 0
    local topLayers <const> = sprite.layers
    local lenTopLayers <const> = #topLayers
    local h = 0
    while h < lenTopLayers do
        h = h + 1
        tally = layerHierarchy(topLayers[h], tally, idLayerDict)
    end
    return idLayerDict
end

local dlg <const> = Dialog { title = "Layer Rename" }

dlg:entry {
    id = "nameEntry",
    label = "Name:",
    focus = true,
    text = "Layer"
}

dlg:newrow { always = false }

dlg:check {
    id = "reverse",
    label = "Order:",
    text = "&Reverse",
    selected = false
}

dlg:newrow { always = false }

dlg:button {
    id = "confirm",
    text = "&OK",
    focus = false,
    onclick = function()
        local sprite <const> = app.sprite
        if not sprite then return end

        local range <const> = app.range
        if range.sprite ~= sprite then return end

        local rangeLayers <const> = range.layers
        local lenRangeLayers <const> = #rangeLayers

        if lenRangeLayers <= 0 then return end

        local args <const> = dlg.data
        local nameEntry <const> = args.nameEntry --[[@as string]]
        local reverse <const> = args.reverse --[[@as boolean]]

        if lenRangeLayers <= 1 then
            app.transaction("Rename Layer", function()
                rangeLayers[1].name = nameEntry
            end)
            app.refresh()
            return
        end

        ---@type Layer[]
        local sortedLayers <const> = {}
        local j = 0
        while j < lenRangeLayers do
            j = j + 1
            sortedLayers[j] = rangeLayers[j]
        end

        local idLayerDict <const> = spriteHierarchy(sprite)
        table.sort(sortedLayers, function(a, b)
            return idLayerDict[a.id] < idLayerDict[b.id]
        end)

        local format <const> = "%s %d"
        local strfmt <const> = string.format
        local lenSortedLayers <const> = #sortedLayers

        app.transaction("Rename Layers", function()
            local i = 0
            while i < lenSortedLayers do
                i = i + 1
                local layer <const> = sortedLayers[i]
                local n <const> = reverse
                    and lenSortedLayers + 1 - i
                    or i
                layer.name = strfmt(format, nameEntry, n)
            end
        end)

        app.refresh()
    end
}

dlg:button {
    id = "cancel",
    text = "&CANCEL",
    focus = false,
    onclick = function()
        dlg:close()
    end
}

dlg:show {
    autoscrollbars = true,
    wait = false
}

Even in the simple case, where the layers in the range are selected sequentially (the Shift key is not held down when the user picks layers) and all the layers share the same parent, range layers need to be loaded into a separate table and sorted. In the simple case, stackIndex can be used as a sort criterion. This thread is where a method for counting layers recursively was previously discussed.

2 Likes