diff --git a/docs/index.rst b/docs/index.rst index bfb3156..6e98931 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -49,7 +49,7 @@ The following code will create this UI: -- generate some assets (below) function love.load() snd = generateClickySound() - normal, hovered, active = generateImageButton() + normal, hovered, active, mask = generateImageButton() smallerFont = love.graphics.newFont(10) end @@ -128,13 +128,14 @@ The following code will create this UI: -- put an image button below the nested cell -- the size of the cell will be 200 by 100 px, -- but the image may be bigger or smaller - -- the button shows the image `normal' when the mouse is outside the image - -- or above a transparent pixel - -- the button shows the image `hovered` if the mouse is above an opaque pixel - -- of the image `normal' + -- the button shows the image `normal' when the button is inactive + -- the button shows the image `hovered` if the mouse is over an opaque pixel + -- of the ImageData `mask` -- the button shows the image `active` if the mouse is above an opaque pixel - -- of the image `normal' and the mouse button is pressed - suit.ImageButton(normal, {hovered = hovered, active = active}, suit.layout:row(200,100)) + -- of the ImageData `mask` and the mouse button is pressed + -- if `mask` is omitted, the alpha-test will be swapped for a test whether + -- the mouse is in the area occupied by the widget + suit.ImageButton(normal, {mask = mask, hovered = hovered, active = active}, suit.layout:row(200,50)) -- if the checkbox is checked, display a precomputed layout if chk.checked then @@ -200,17 +201,17 @@ The following code will create this UI: local d2 = math.exp(-((px+.7)^2 + (py+.1)^2) * 2) local d = (d1 + d2)/2 if d > t then - return r,g,b, 255 * ((d-t) / (1-t))^.2 + return r,g,b, ((d-t) / (1-t))^.2 end return 0,0,0,0 end end local normal, hovered, active = love.image.newImageData(200,100), love.image.newImageData(200,100), love.image.newImageData(200,100) - normal:mapPixel(metaballs(.48, 188,188,188)) - hovered:mapPixel(metaballs(.46, 50,153,187)) - active:mapPixel(metaballs(.43, 255,153,0)) - return love.graphics.newImage(normal), love.graphics.newImage(hovered), love.graphics.newImage(active) + normal:mapPixel(metaballs(.48, .74,.74,.74)) + hovered:mapPixel(metaballs(.46, .2,.6,.6)) + active:mapPixel(metaballs(.43, 1,.6,0)) + return love.graphics.newImage(normal), love.graphics.newImage(hovered), love.graphics.newImage(active), normal end Indices and tables diff --git a/docs/widgets.rst b/docs/widgets.rst index c0f18eb..6ffe7cf 100644 --- a/docs/widgets.rst +++ b/docs/widgets.rst @@ -30,7 +30,7 @@ Creates a label at position ``(x,y)`` with width ``w`` and height ``h``. .. function:: ImageButton(normal, options, x,y) - :param Image normal: Image of the button in normal state. + :param mixed normal: Image of the button in normal state. :param table options: Widget options. :param numbers x,y: Upper left corner of the widget. :returns: Return state (see below). @@ -39,13 +39,21 @@ Creates an image button widget at position ``(x,y)``. Unlike all other widgets, an ``ImageButton`` is not affected by the current theme. The argument ``normal`` defines the image of the normal state as well as the -area of the widget: The button activates when the mouse is over a pixel with -non-zero alpha value. +area of the widget. +The button activates when the mouse enters the area occupied by the widget. +If the option ``mask`` defined, the button activates only if the mouse is over +a pixel with non-zero alpha. +You can provide additional ``hovered`` and ``active`` images, but the widget area +is always computed from the ``normal`` image. You can provide additional ``hovered`` and ``active`` images, but the widget area is always computed from the ``normal`` image. **Additional Options:** +``mask`` + Alpha-mask of the button, i.e. an ``ImageData`` of the same size as the + ``normal`` image that has non-zero alpha where the button should activate. + ``normal`` Image for the normal state of the widget. Defaults to widget payload. diff --git a/imagebutton.lua b/imagebutton.lua index 0cf544a..cf7483a 100644 --- a/imagebutton.lua +++ b/imagebutton.lua @@ -2,36 +2,49 @@ local BASE = (...):match('(.-)[^%.]+$') +local function isType(val, typ) + return type(val) == "userdata" and val.typeOf and val:typeOf(typ) +end + return function(core, normal, ...) local opt, x,y = core.getOptionsAndSize(...) opt.normal = normal or opt.normal or opt[1] opt.hovered = opt.hovered or opt[2] or opt.normal opt.active = opt.active or opt[3] or opt.hovered - assert(opt.normal, "Need at least `normal' state image") opt.id = opt.id or opt.normal - opt.state = core:registerMouseHit(opt.id, x,y, function(u,v) - local id = opt.normal:getData() - assert(id:typeOf("ImageData"), "Can only use uncompressed images") + local image = assert(opt.normal, "No image for state `normal'") + + core:registerMouseHit(opt.id, x, y, function(u,v) + -- mouse in image? u, v = math.floor(u+.5), math.floor(v+.5) - if u < 0 or u >= opt.normal:getWidth() or v < 0 or v >= opt.normal:getHeight() then + if u < 0 or u >= image:getWidth() or v < 0 or v >= mask:getHeight() then return false end - local _,_,_,a = id:getPixel(u,v) - return a > 0 + + if opt.mask then + -- alpha test + assert(isType(opt.mask, "ImageData"), "Option `mask` is not a love.image.ImageData") + assert(u < mask:getWidth() and v < mask:getHeight(), "Mask may not be smaller than image.") + local _,_,_,a = mask:getPixel(u,v) + return a > 0 + end + + return true end) - local img = opt.normal if core:isActive(opt.id) then - img = opt.active + image = opt.active elseif core:isHovered(opt.id) then - img = opt.hovered + image = opt.hovered end - core:registerDraw(opt.draw or function(img,x,y, r,g,b,a) + assert(isType(image, "Image"), "state image is not a love.graphics.image") + + core:registerDraw(opt.draw or function(image,x,y, r,g,b,a) love.graphics.setColor(r,g,b,a) - love.graphics.draw(img,x,y) - end, img, x,y, love.graphics.getColor()) + love.graphics.draw(image,x,y) + end, image, x,y, love.graphics.getColor()) return { id = opt.id,