API: Add mouse enter/exit events to Dialog:canvas() component
Did other users suggest similar ideas?
- No
- Yes/Links to similar ideas
Is your feature request related to a problem? Please describe.
In an extension I am developing, I wanted to use some custom UI using the Dialog:canvas() component, but noticed that the mouse position isn't updated any more if the mouse isn't actually over the canvas, and onmouseover isn't being called any more.
In the example script below I save and update the mouse's position when onmousemove is called, making a responsive design. However, when a button is touching the edge of the canvas, it will still be considered hovered over on the last time the onmousemove is being called when you move the mouse out of the canvas.
local mouse = { position = Point(-1, -1), leftClick = false }
local states = {
normal = "button_normal",
hot = "button_hot",
pushed = "buttonpushed",
focused = "button_focused"
}
local bounds = Rectangle(0, 0, 100, 20)
local text = "Not initialized"
local dlg = Dialog("Dialog"):separator{ text = "Button in a canvas" }
dlg:canvas{
width = bounds.w,
height = bounds.h,
onpaint = function (ev)
local ctx = ev.context
local state = states.normal
text = "Idle"
local isMouseOver = bounds:contains(mouse.position)
if isMouseOver then
state = states.hot or state
text = "Mouse pos.: (" .. mouse.position.x .. ", " .. mouse.position.y .. ")"
if mouse.leftClick then
state = states.pushed
text = "Mouse clicked"
end
end
dlg:modify{ id = "text", text = text }
ctx:drawThemeRect(state, bounds)
end,
onmousemove = function (ev)
mouse.position = Point(ev.x, ev.y)
dlg:repaint()
end,
onmousedown = function (ev)
mouse.leftClick = ev.button == MouseButton.LEFT
dlg:repaint()
end,
onmouseup = function (_)
if not mouse.leftClick then
return
end
mouse.leftClick = false
dlg:repaint()
end
}
:label{ id = "text",text = text }
:separator{ text = "Normal buttons" }
:button{ height = bounds.h, width = bounds.w, onclick = function (_) end }
:newrow()
:button{ height = bounds.h, width = bounds.w, onclick = function (_) end }
dlg:show{ wait = true }https://github.com/user-attachments/assets/0c9aeac1-598c-413c-b35d-4c7539508c46
Describe the solution you'd like
In my case, an event that triggers when the mouse exits the canvas could reset the saved mouse position to something that will be outside of any of the custom UI elements.
Add an event on the Dialog:canvas() component called something like onmouseexit or onmouseout. To keep it consistent, also add a onmouseenter or onmousein event, to be set/reset something as soon as the mouse enters the canvas.
dlg:canvas{
-- ...
onmouseexit = function (ev)
mouse.position = Point(-1, -1)
end
}Describe alternatives you've considered
This alternative uses a Timer to reset the mouse position after 0.5 seconds of no onmousemove event call.
-- ...
local dlg = Dialog("Dialog"):separator{ text = "Button in a canvas" }
local timer
timer = Timer{
interval = 0.5,
ontick = function ()
mouse.position = Point(-1, -1)
dlg:repaint()
timer:stop()
end
}
dlg:canvas{
width = bounds.w,
-- ...
onmousemove = function (ev)
mouse.position = Point(ev.x, ev.y)
timer:start()
dlg:repaint()
end,
-- ...This results in delayed reset of the UI, sometimes even while the mouse technically is still hovering over the UI element, which is not the best experience.
https://github.com/user-attachments/assets/9da6532e-b15a-43e8-9c25-573e87ce7741
Source: aseprite/aseprite