Dialog does not instantiate

Hello and thank you in advance for your time and help.
I try to rewrite my tool ring script so it can be modified more easily I got most of it working but I can’t get to make it happen that each tool widget is displayed in it’s own dialog, instead all widgets show in the first created dialog.

local mouse = {position = Point(0, 0), leftClick = false}
local focusedWidget = nil

-- Define the Widget class
Widget = {bounds = nil, state = nil, text = nil, icon = nil, onclickl = nil, onclickr = nil, tooldlg = nil}

function Widget:new(o, bounds, state, text, icon, color, onclickl, onclickr, tooldlg)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    self.bounds = bounds
    self.state = state
    self.text = text
    self.icon = icon
    self.color = color
    self.onclickl = onclickl
    self.onclickr = onclickr
    self.tooldlg = tooldlg
    return o
end

function Widget:draw(ctx)
    local state = self.state.normal
    if self == focusedWidget then
        state = self.state.focused
    end

    local isMouseOver = self.bounds:contains(mouse.position)
    if isMouseOver then
        state = self.state.hot or state
        if mouse.leftClick then
            state = self.state.selected
        end
    end

    ctx:drawThemeRect(state.part, self.bounds)

    local center = Point(self.bounds.x + self.bounds.width / 2,
                         self.bounds.y + self.bounds.height / 2)

    if self.icon then
        local size = Rectangle(0, 0, 16, 16) -- Assuming default icon size of 16x16 pixels
        ctx:drawThemeImage(self.icon, center.x - size.width / 2,
                           center.y - size.height / 2)
    elseif self.text then
        local size = ctx:measureText(self.text)
        ctx.color = app.theme.color[state.color]
        ctx:fillText(self.text, center.x - size.width / 2,
                     center.y - size.height / 2)
    elseif self.color then
        local size = self:shrinkSize(self.bounds, 6)
        ctx.color = self.color
        ctx:roundedRect(size, 8)
        ctx:stroke()
        ctx:fill()
    end
end

function Widget:handleMouse(ev)
    local isMouseOver = self.bounds:contains(mouse.position)
    if isMouseOver then
        if mouse.leftClick then
            self.onclickl()
            focusedWidget = self
        elseif mouse.rightClick then
            self.onclickr()
            focusedWidget = self
        end
    end
end

function Widget:shrinkSize(rect, size)
    return Rectangle(rect.x + 0.5*size, rect.y + 0.5*size, rect.width - size, rect.height - size)
end

function Widget:show(bounds)
    self.tooldlg:show(self.tooldlg.dlg,bounds)
end

function Widget:close()
    self.tooldlg:close(self.tooldlg.dlg)
end

-- Define tool dialog class
ToolDialog = {titl = nil, bounds = nil, dlg = nil, canvas = nil}
function ToolDialog:new(o, title, bounds)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    self.title = title
    self.bounds = bounds
    return o
end

function ToolDialog:show(dlg,bounds)
            dlg:show{
                wait=false,
                bounds=bounds,
                autoscrollbars=false,
              }
end

function ToolDialog:create(title, bounds, widget)
    local dlg = Dialog(title)
    --dlg:modify{onclose = function() self:close() end}
    self.canvas = dlg:canvas{id = title,
    width = bounds.width,
    height = bounds.height,
    onpaint = function(ev)
        local ctx = ev.context
        widget:draw(ctx)
    end,
    onmousemove = function(ev)
        mouse.position = Point(ev.x, ev.y)
        dlg:repaint()
    end,
    onmousedown = function(ev)
        mouse.leftClick = ev.button == MouseButton.LEFT
        mouse.rightClick = ev.button == MouseButton.RIGHT
        widget:handleMouse(ev)
        dlg:repaint()
    end,
    onmouseup = function(ev)
        if ev.button == MouseButton.LEFT then
            mouse.leftClick = false
        end
        if ev.button == MouseButton.RIGHT then
            mouse.rightClick = false
        end
        dlg:repaint()
    end
    }
    self.dlg = dlg
end

function ToolDialog:close(dlg)
    dlg:close()
end

function createTool(o, title, onclickl, onclickr)
-- Create instance of the Tooldialog class
local dlg = ToolDialog:new(nil, title, Rectangle(0,0, BUTTON_SIZE, BUTTON_SIZE))
-- Create instances of the Widget class
local widget = Widget:new(o, Rectangle(0, 0, BUTTON_SIZE, BUTTON_SIZE), {
    normal = {part = "button_normal", color = "button_normal_text"},
    focused = {part = "button_focused", color = "button_normal_text"},
    hot = {part = "button_hot", color = "button_hot_text"},
    selected = {part = "button_selected", color = "button_selected_text"},
    -- Other states...
}, nil, nil,app.fgColor,nil,nil,dlg )
widget.onclickl = onclickl
widget.onclickr = onclickr
dlg:create(title, dlg.bounds, widget)
return widget
end

--Instantiate Tools
local myTool = {}
myTool = createTool(myTool, "Pick Color", function()app.fgColor = myTool.color end, function() myTool.color = app.fgColor end)
local myOtherTool ={}
myOtherTool = createTool(myOtherTool, "Pick Another Color", function()app.fgColor = myTool.color end, function() myTool.color = app.fgColor end)


myTool:show(Rectangle(500, 100, 180, 140))
myOtherTool:show(Rectangle(300, 80, 80, 80))

I’m not so familiar with LUA so if there are other improvements that could be also done, I would be happy for any feedback.

This does not address any specific bug.

As general advice, I recommend getting a tool like Lua Language Server along with a type definition for Aseprite. Then, I recommend reading the LLS wiki on annotations. If a project is complex enough that it merits OOP, then take time to annotate and document your classes and methods.

The type definition autocomplete will help you keep track of what type of objects you’re working with and what methods they can call. Comments in a good type definition will also try to alert you to the many gotchas in Aseprite’s API, for example that Rectangles and Sizes can be zero or negative.

I personally find Lua to be a pain to debug, so I try to ‘code defensively’. Turn off automatic casting between numbers and integers, so that you’re forced to recognize when self.bounds.width // 2 is different from self.bounds.width / 2 and 1 << 4 is different from 2 ^ 4. Use the <const> keyword to reduce issues with mutability. For example, local x <const> = 5 would cause the later reassignment x = 7 to raise an error. Annotating your methods will help you recognize when something could be nil and you therefore have to check and provide a fallback. Ideally, you would want to minimize the amount of nil-able objects.

You can assign a shortcut to Aseprite’s Developer Console under Edit > Keyboard Shortcuts. The console tab can be dragged and dropped so it sits side by side with a sprite tab. I personally find print statements easier than the Debugger under File > Scripts, but there’s that, too.

Thank you so much for this detailed and thoughtful reply, I really appreciate it.

It was never thought to be a bug report more a call for help of more knowledgeable people, im sorry if it was understood as firstly.

I will try again with all these suggestions, Aseprite is a great tool and im really looking forward to make a script that could help me and hopefully others interact with it in a different way making it even more likeable to use in future.