How do I add a custom icon to the macOS menu bar? [duplicate]
I am aware of the Script Editor menu. That is not what I'm looking for.
Is there a way to run an Applescript from the menu bar? I can save it as a file and run it from the Script Editor menu, and I can save it as an application and stick it in my dock. Is there a file format that allows it to run from the menu bar, as many applications do? Or a way to edit the code that does the same thing?
Cocoa-AppleScript as a script or AppleScript application created in Script Editor supports Menu Bar app. This script runs on macOS 10.12 (maybe runs on 10.10.x, 10.11.x). This script can be saved as application.
--AppleScript: menu bar script -- Created 2017-03-03 by Takaaki Naganoya
--2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
--http://piyocast.com/as/archives/4502
property aStatusItem : missing value
on run
init() of me
end run
on init()
set aList to {"Piyomaru", "Software", "", "Takaaki", "Naganoya", "", "Quit"}
set aStatusItem to current application's NSStatusBar's systemStatusBar()'s statusItemWithLength:(current application's NSVariableStatusItemLength)
aStatusItem's setTitle:"🚗"
aStatusItem's setHighlightMode:true
aStatusItem's setMenu:(createMenu(aList) of me)
end init
on createMenu(aList)
set aMenu to current application's NSMenu's alloc()'s init()
set aCount to 1
repeat with i in aList
set j to contents of i
if j is not equal to "" then
set aMenuItem to (current application's NSMenuItem's alloc()'s initWithTitle:j action:"actionHandler:" keyEquivalent:"")
else
set aMenuItem to (current application's NSMenuItem's separatorItem())
end if
(aMenuItem's setTarget:me)
(aMenuItem's setTag:aCount)
(aMenu's addItem:aMenuItem)
if j is not equal to "" then
set aCount to aCount + 1
end if
end repeat
return aMenu
end createMenu
on actionHandler:sender
set aTag to tag of sender as integer
set aTitle to title of sender as string
if aTitle is not equal to "Quit" then
display dialog aTag as string
else
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
end if
end actionHandler:
I previously edited the original code, posted by Piyomaru, just to the point it would compile and wanted to add some additional information to make the example code more useful for anyone relatively new to AppleScript.
Presently in the example code, if saved as an application it has no code to quit the application, it only removes the menu bar object. So, in the actionHandler
handler of the script, add the following to the else
branch of the if
statement, after the existing code:
if (name of current application) is not "Script Editor" then
tell current application to quit
end if
It's coded so if/when run from within Script Editor it will not attempt to close Script Editor. You can use it without the if
statement block and use just tell current application to quit
if you're not going to run it from within Script Editor.
Because this script is using Cocoa-AppleScript, once run from within Script Editor, it must be compiled again just before saving it in order to flush the C and Objective-C pointers created by the Cocoa-Applescript code, since pointers cannot be saved in the script. Just click the hammer icon on the Toolbar, or: Script Editor > Script > Compile ⌘K
Additionally, when saved as an application you must check the Stay open after run handler check-box when saving it, and by default the app's Dock Tile will show in the Dock when running. As this might be unwanted behavior for a menu bar app, use the following command in Terminal to add the necessary key to the app's Info.plist file so it will not show in the Dock.
defaults write /Applications/name_of_app.app/Contents/Info.plist LSUIElement -bool yes
- Replace
name_of_app
with the actual name you saved it as, and if necessary change the path if not in/Applications
. - Note that if you drag the app to the Dock, its Dock Tile will still show on the Dock, just won't have a open indicator under it when open. This added key to the Info.plist file is to keep the Dock Tile from showing on the Dock when running, when the app has not been dragged to the Dock in the first place.
In the actionHandler
handler of the original example code, it just displays a dialog box with the menu item number that was clicked, which certainly fine for example code, however, I'd like to show a different example how to have separate code execute based on which menu item was clicked. The following example code could replace the entire actionHandler
handler in the original script:
on actionHandler:sender
set aTitle to title of sender as string
if aTitle is equal to "Quit" then
current application's NSStatusBar's systemStatusBar()'s removeStatusItem:aStatusItem
if (name of current application) is not "Script Editor" then
tell current application to quit
end if
else if aTitle is equal to "Piyomaru" then
-- # Your code to run for this menu choice goes here:
else if aTitle is equal to "Software" then
-- # Your code to run for this menu choice goes here:
else if aTitle is equal to "Takaaki" then
-- # Your code to run for this menu choice goes here:
else if aTitle is equal to "Naganoya" then
-- # Your code to run for this menu choice goes here:
end if
end actionHandler:
Now this could be written differently, i.e. "Quit" as an else
or else if
at the bottom, but I did it this way because I want all other menu commands to be an else if
choice, even though logically any other given menu command is more likely to be clicked then "Quit". For me, I think it's easier to add/edit/remove the menu command's code when written this way. It's just a personal preference, so do it as you feel what's right for you.
A discussion note from the AppleScript Language Guide:
An if statement can contain any number of else if clauses; AppleScript looks for the first Boolean expression contained in an if or else if clause that is true, executes the statements contained in its block (the statements between one else if and the following else if or else clause), and then exits the if statement.
An if statement can also include a final else clause. The statements in its block are executed if no other test in the if statement passes.
For the most part of the original example code, you should only have to modify two line of code in the init
handler:
set aList to {"Piyomaru", "Software", "", "Takaaki", "Naganoya", "", "Quit"}
- This sets the name of the menu commands in their order, and
""
is for a separator.
For a different icon:
aStatusItem's setTitle:"🚗"
- Change the "🚗" to something else you prefer, from: Script Editor > Edit > Emoji & Symbols
Hopefully it's obvious that the if
statement block in the actionHandler
handler will correspond to the aList
in the init
handler, so match the names in the aTitle is equal to
in the actionHandler
handler to the names in the aList
in the init
handler.
The rest of the code should not need to be modified, although it certainly can if need be or wanted.
Normal AppleScript as a script or AppleScript application created in Script Editor is not supported as a Menu Bar app. You'd have to use Cocoa-AppleScript or build an app in Xcode (or use a third-party launcher) if you don't want to use the Script menu in the Menu bar.
Here's an example app done in Xcode using Swift: WeatherBar.
One long time trusted app that does this is FastScripts, and although it's looks like the Scripts menu it does offer additional functionality. It can be run as a free app, up to 10 keyboard shortcuts, or upgraded for $9.95 to unlock unlimited keyboard shortcuts.
I have no affiliation with Red Sweater Software, LLC, other then as a user of FastScripts as a free app.