Hammerspoon / hammerspoon

Staggeringly powerful macOS desktop automation with Lua
http://www.hammerspoon.org
MIT License
12.09k stars 582 forks source link

Canvas become transparent when it was shown from hidden state #2809

Open hououinkami opened 3 years ago

hououinkami commented 3 years ago

I create some canvas objects and show them, then I used hide method to hide them. When I show them by show method, these canvas become transparent just like the alpha value changed. But if I used delete method instead of hide, it works well. Is this a bug? Thanks very much!

latenitefilms commented 3 years ago

Ummm, this test seems to work fine?

c = require("hs.canvas")
a = c.new{x = 100, y = 100, w = 200, h = 200 }:show()
a[1] = {
  type="image",
  -- the Hammerspoon menu icon has this name within the Application bundle, so we can get it this way:
  image = hs.image.imageFromName("statusicon"):template(false),
  imageScaling = "scaleToFit"
}
a[2] = {
  type = "rectangle",
  action = "fill",
  fillGradientColors = {
    { red = 1 },
    { red = 1, green = .65 },
    { red = 1, green = 1 },
    { green = 1 },
    { blue = 1 },
    { red = .30, blue = .5 },
    { red = .93, green = .5, blue = .93 }
  },
  fillGradient = "radial"
}

hs.timer.doAfter(2, function() a:hide() end)
hs.timer.doAfter(4, function() a:show() end)
hs.timer.doAfter(6, function() a:delete() end)
hououinkami commented 3 years ago

Ummm, this test seems to work fine?

c = require("hs.canvas")
a = c.new{x = 100, y = 100, w = 200, h = 200 }:show()
a[1] = {
  type="image",
  -- the Hammerspoon menu icon has this name within the Application bundle, so we can get it this way:
  image = hs.image.imageFromName("statusicon"):template(false),
  imageScaling = "scaleToFit"
}
a[2] = {
  type = "rectangle",
  action = "fill",
  fillGradientColors = {
    { red = 1 },
    { red = 1, green = .65 },
    { red = 1, green = 1 },
    { green = 1 },
    { blue = 1 },
    { red = .30, blue = .5 },
    { red = .93, green = .5, blue = .93 }
  },
  fillGradient = "radial"
}

hs.timer.doAfter(2, function() a:hide() end)
hs.timer.doAfter(4, function() a:show() end)
hs.timer.doAfter(6, function() a:delete() end)

Thanks for your reply! I replace your example code into mine (function togglecanvas()) as follows.

c = require("hs.canvas")
function setCanvas()
    a = c.new{x = 100, y = 100, w = 200, h = 200 }
    a[1] = {
        type="image",
        -- the Hammerspoon menu icon has this name within the Application bundle, so we can get it this way:
        image = hs.image.imageFromName("statusicon"):template(false),
        imageScaling = "scaleToFit"
    }
    a[2] = {
        type = "rectangle",
        action = "fill",
        fillGradientColors = {
            { red = 1 },
            { red = 1, green = .65 },
            { red = 1, green = 1 },
            { green = 1 },
            { blue = 1 },
            { red = .30, blue = .5 },
            { red = .93, green = .5, blue = .93 }
        },
    fillGradient = "radial"
    }
end
function togglecanvas()
    if a:isShowing() == true then
        a:hide(0.6)
    else
        a:show(0.6)
    end
end
setCanvas()
testBar = hs.menubar.new()
testBar:setTitle('TEST')
testBar:setClickCallback(togglecanvas)

The problem occurred, especially when the togglecanvas function is triggered frequently. But if I delete the fadeOutTime parameter, it seems to work well. So this problem is caused by fadeOutTime parameter?

latenitefilms commented 3 years ago

Yes, it looks like things break if you try and hide or show it when it's already in the process of fading to hide or show.

You could add some logic to work around it, for example:

c = require("hs.canvas")
function setCanvas()
    a = c.new{x = 100, y = 100, w = 200, h = 200 }
    a[1] = {
        type="image",
        -- the Hammerspoon menu icon has this name within the Application bundle, so we can get it this way:
        image = hs.image.imageFromName("statusicon"):template(false),
        imageScaling = "scaleToFit"
    }
    a[2] = {
        type = "rectangle",
        action = "fill",
        fillGradientColors = {
            { red = 1 },
            { red = 1, green = .65 },
            { red = 1, green = 1 },
            { green = 1 },
            { blue = 1 },
            { red = .30, blue = .5 },
            { red = .93, green = .5, blue = .93 }
        },
    fillGradient = "radial"
    }
end

local fadeTime = 0.6
local isFading = false

function togglecanvas()
    if isFading then
        print("already fading")
        return
    end

    isFading = true

    if a:isShowing() == true then       
        a:hide(fadeTime)
    else        
        a:show(fadeTime)
    end

    hs.timer.doAfter(fadeTime, function() isFading = false end)
end

setCanvas()
testBar = hs.menubar.new()
testBar:setTitle('TEST')
testBar:setClickCallback(togglecanvas)

The above code is currently just "ignoring" the clicks if a fade is already in action, but you could add another timer to delay the show/hide until after the first one is complete.

@asmagill - Would you consider this a bug?

hououinkami commented 3 years ago

Use timer to delay the fade action works better, thanks very much! I test this code and the problem occurs randomly at a lower frequency.