I was messing around a bit earlier, and I came up with a simple way to create non-rectangular hotspots using the pixel-sensitive hotspots recently added. Below is a rather cluttered example, very slightly based on my miniwidget framework. It creates a triangular hotspot over one half of the window, over which the cursor turns into a crosshair.
local point_in_polygon = function(x, y, points)
assert(#points % 2 == 0, "Unfinished point pair")
-- below loop (and just this loop) adapted from:
-- http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
local i, j, c = 1, #points-1, false
while i < #points do
local ix, iy = points[i], points[i+1]
local jx, jy = points[j], points[j+1]
local test = ((iy > y) ~= (jy > y)) and
(x < (jx - ix) * (y - iy) / (jy - iy) + ix)
if test then
c = not c
end
j, i = i, i + 2
end
-- If it's not fully within the polygon, check the sides.
-- Compare the slope of each side against the
-- slope of the point and a vertice.
if not c then
i, j = 1, #points-1
while i < #points do
local ix, iy = points[i], points[i+1]
local jx, jy = points[j], points[j+1]
local line_slope =
(ix ~= jx) and (jy - iy) / (jx - ix) or nil
local test_slope =
(ix ~= x) and (y - iy) / (x - ix) or nil
-- If the slopes match, also check that the point
-- lies on the line segment itself.
-- Only check one axis, since the slope will guarantee
-- the other.
if line_slope == test_slope and
x <= math.max(ix, jx) and x >= math.min(ix, jx) then
c = true
break
end
j, i = i, i + 2
end
end
return c
end
local windows = {[0] = 0}
local mouse_event = function(event)
return function(flags, id)
local w = windows[id]
local x, y = WindowInfo(id, 14), WindowInfo(id, 15)
for _,hotspot in ipairs(w.children) do
if point_in_polygon(x, y, hotspot.points) then
local func = hotspot.event_handlers[event]
if func then func() end
break
end
end
end
end
HotspotCallbacks = {
mousedown = mouse_event("mousedown"),
cancelmousedown = mouse_event("cancelmousedown"),
mouseup = mouse_event("mouseup"),
mouseover = mouse_event("mouseover"),
cancelmouseover = mouse_event("cancelmouseover"),
}
Window = {
new = function(width, height)
local w = {}
local name = GetPluginID() .. "_" .. (windows[0]+1)
w.name = name
w.width = width
w.height = height
w.children = {}
w.event_handlers = {}
WindowCreate(name, 0, 0, width, height, 0, 2, ColourNameToRGB("white"))
WindowShow(name, true)
WindowAddHotspot(name, name, 0, 0, width, height,
"HotspotCallbacks.mouseover",
"HotspotCallbacks.cancelmouseover",
"HotspotCallbacks.mousedown",
"HotspotCallbacks.cancelmousedown",
"HotspotCallbacks.mouseup",
"", 0, 1)
windows[name] = w
windows[0] = windows[0] + 1
return w
end,
}
w = Window.new(50, 50)
table.insert(w.children, {
points = {
0,0,
50,0,
50,50,
},
event_handlers = {
mouseover = function()
SetCursor(3)
end,
cancelmouseover = function()
SetCursor(0)
end,
},
}
)
If you were to abstract it away a bit further, it's easy to see how one could have hotspots based on distance from a central point (circle) or other more complex hotspots. |