Hammerspoon: hs.menubar.new(false) appearing in system menubar?

Created on 3 Oct 2017  路  7Comments  路  Source: Hammerspoon/hammerspoon

I have a hs.menubar "attached" to a hs.chooser, so that when I right click on the hs.chooser it pops up the menubar. I've noticed though, that ever now and again, even though I'm using hs.menubar.new(false), the menubar will appear in the system menubar. It doesn't have any text value or icon, so you'd only notice if you click up there, or if another application has something in the menubar and you notice the weird space between the icons - but it's a little... odd.

Possibly related to #1566?

Most helpful comment

Sorry must have missed this before... FWIW I am working on a replacement for hs.menubar as part of the guitk suite that I'm hoping to move to core soon. With the new menubar module, creating a menu (unless using the legacy wrapper) is independent of creating the actual item which appears in the menubar at the top of the screen (technically its an NSStatusItem), so this particular issue should be a non-issue, hopefully soon.

All 7 comments

I can reproduce this with:

-- hs.chooser Example

local inspect = require("hs.inspect")

local logger = require("hs.logger")
logger.defaultLogLevel = 'debug'
log = logger.new("chooser")

local chooser = require("hs.chooser")
local menubar = require("hs.menubar")
local mouse = require("hs.mouse")

showAChoice = true
numberOfChoices = 100000

function choicesA()
    local result = {}

    for i=1, numberOfChoices, 1 do
        table.insert(result, {
            ["text"] = "Choice " .. tostring(i),
            ["subText"] = "This is the subtext of choice " .. tostring(i),
            ["uuid"] = tostring(i)
        })
    end

    return result
end

function choicesB()
    local result = {}

    for i=1, numberOfChoices, 1 do
        table.insert(result, {
            ["text"] = "New Choice " .. tostring(i),
            ["subText"] = "This is the subtext of choice " .. tostring(i),
            ["uuid"] = tostring(i)
        })
    end

    return result
end

myMenubar = menubar.new(false)
    :setMenu({
       {
        title = "Refresh",
        fn = function()
            log.df("Refreshing Choices.")
            if showAChoice then
                choices = choicesB()
            else
                choices = choicesA()
            end
            showAChoice = not showAChoice
            myChooser:refreshChoicesCallback()
        end
       },
   })

function completionFn(value)
    log.df("completionFn value: %s", inspect(value))
end

function choicesFn(value)
    if not choices then
        choices = choicesA()
    end
    return choices
end

function rightClickFn()
    local point = mouse.getAbsolutePosition()
    myMenubar:popupMenu(point)
end

function queryChangedFn(value)
    log.df("queryChangedFn value: %s", inspect(value))
end

function showFn()
    log.df("showFn triggered.")
end

myChooser = chooser.new(completionFn)
    :choices(choicesFn)
    --:queryChangedCallback(queryChangedFn)
    :showCallback(showFn)
    :rightClickCallback(rightClickFn)
    :show()

screen shot 2017-10-05 at 5 11 45 pm

This however works fine:

-- hs.chooser Example

local inspect = require("hs.inspect")

local logger = require("hs.logger")
logger.defaultLogLevel = 'debug'
log = logger.new("chooser")

local chooser = require("hs.chooser")
local menubar = require("hs.menubar")
local mouse = require("hs.mouse")

showAChoice = true
numberOfChoices = 100000

function choicesA()
    local result = {}

    for i=1, numberOfChoices, 1 do
        table.insert(result, {
            ["text"] = "Choice " .. tostring(i),
            ["subText"] = "This is the subtext of choice " .. tostring(i),
            ["uuid"] = tostring(i)
        })
    end

    return result
end

function choicesB()
    local result = {}

    for i=1, numberOfChoices, 1 do
        table.insert(result, {
            ["text"] = "New Choice " .. tostring(i),
            ["subText"] = "This is the subtext of choice " .. tostring(i),
            ["uuid"] = tostring(i)
        })
    end

    return result
end

myMenubar = menubar.new()
    :setMenu({
       {
        title = "Refresh",
        fn = function()
            log.df("Refreshing Choices.")
            if showAChoice then
                choices = choicesB()
            else
                choices = choicesA()
            end
            showAChoice = not showAChoice
            myChooser:refreshChoicesCallback()
        end
       },
   })
   :removeFromMenuBar()

function completionFn(value)
    log.df("completionFn value: %s", inspect(value))
end

function choicesFn(value)
    if not choices then
        choices = choicesA()
    end
    return choices
end

function rightClickFn()
    local point = mouse.getAbsolutePosition()
    myMenubar:popupMenu(point)
end

function queryChangedFn(value)
    log.df("queryChangedFn value: %s", inspect(value))
end

function showFn()
    log.df("showFn triggered.")
end

myChooser = chooser.new(completionFn)
    :choices(choicesFn)
    --:queryChangedCallback(queryChangedFn)
    :showCallback(showFn)
    :rightClickCallback(rightClickFn)
    :show()

So the trick is you need to use menubar.new() and call :removeFromMenuBar() directly AFTER :setMenu().

Can anyone else reproduce?

I'm also getting an issue which each time I reload Hammerspoon, my system menubar item gets further and further away from the other icons.

I wonder if there's something funky in hs.menubar's garbage collection?

The code does have this note:

TODO: Should we keep a registry of menubar items and clean them up here? They ought to have been __gc'd by this point.

Maybe related? Any ideas @asmagill ?

screen shot 2017-10-21 at 12 07 47 pm

Sorry must have missed this before... FWIW I am working on a replacement for hs.menubar as part of the guitk suite that I'm hoping to move to core soon. With the new menubar module, creating a menu (unless using the legacy wrapper) is independent of creating the actual item which appears in the menubar at the top of the screen (technically its an NSStatusItem), so this particular issue should be a non-issue, hopefully soon.

To keep things tidy, I'm going to close this issue in favour of #1530.

Given #1530 is probably still a while off, I'm going to reopen this issue.

This code incorrectly shows the popup menu in the system menu bar:

menubar = hs.menubar.new(false)
menubar:setTitle("Hidden Menu")
menubar:setMenu( {
       { title = "my menu item", fn = function() print("you clicked my menu item!") end },
       { title = "-" },
       { title = "other item", fn = some_function },
       { title = "disabled item", disabled = true },
       { title = "checked item", checked = true },
   })
menubar:popupMenu(hs.mouse.getAbsolutePosition(), true)

This code works correctly:

menubar = hs.menubar.new(true)
menubar:setTitle("Hidden Menu")
menubar:setMenu( {
       { title = "my menu item", fn = function() print("you clicked my menu item!") end },
       { title = "-" },
       { title = "other item", fn = some_function },
       { title = "disabled item", disabled = true },
       { title = "checked item", checked = true },
   })
menubar:removeFromMenuBar()   
menubar:popupMenu(hs.mouse.getAbsolutePosition(), true)

Any ideas @cmsj ?

I believe this issue is now fixed, so closing.

Was this page helpful?
0 / 5 - 0 ratings