MacRuby でささっとメニューバーのステータスメニューに常駐するアプリケーションをつくれたら便利だと思いませんか?
メニューバーのステータスメニューとは↓のような領域のことです。
これができると daemontools で常駐とかさせなくても app を start up にいれるだけでいいし、管理が楽です。
この分野では mynu というライブラリが有名なのだが、これはコードが複雑でとりあつかいづらい上に、ドキュメントをよんでも macruby コマンドで走らせようという話しかでてこなくて不安になる。僕がほしいのは .app だ! という気持になるのだ。
というわけで、通常のアプリ開発フローでステータスバーに常駐するアプリをつくるための雛形を作成した。以下のコードを Xcode で生成した雛形の AppDelegate.rb におきかえ、適当な画像(たとえば このへん など)を icon.png としてプロジェクトに追加するだけで OK。
ステータスバーアプリとして必要そうなコードのサンプルはうめこんであります。NSMenuItem を直でいじるとコールバックの登録がめんどうなので DSL っぽくかけるようにしてありますが、instance_eval してるだけなので誰でもよめるコードになっています。
ステータスバーに常駐するアプリだと、ごちゃごちゃ覚えなくても簡単な ruby アプリを常駐させられるし、配布できるので便利です。webrick のアプリを background でうごかして常駐させるとか、そういうのも簡単にできますし、cron っぽく定期的になんかさせる、みたいなのもできますね。まあそういうの daemontools とか launchctl とか cron でやってもいいんですけど、osx っぽくできるし配布しやすいのでよさそうな気がします。
実際のコードは以下のとおりです。
#
# AppDelegate.rb
# StatusBarSample
#
# Created by Tokuhiro Matsuno on 1/4/13.
# Copyright 2013 Tokuhiro Matsuno. All rights reserved.
#
class MenuHelper
attr_reader :menu
def self.create(title, &block)
obj = self.new(title)
obj.instance_eval &block
return obj.menu
end
def initialize(title)
@menu = NSMenu.new()
@menu.initWithTitle('FooApp')
@menu.autoenablesItems = false
end
def item(title, &block)
item = NSMenuItem.new()
item.title = title
# Create anonnymous class to receive event.
receiver = Class.new()
receiver.class_eval do
define_method(:call) do |sender|
block.(sender)
end
end
item.action = 'call:'
item.target = receiver.new
@menu.addItem item
return item
end
def nest(title, &block)
item = NSMenuItem.new()
item.title = title
submenu = MenuHelper.new(title)
item.submenu = submenu.menu
submenu.instance_eval &block
@menu.addItem item
end
def separator
@menu.addItem NSMenuItem.separatorItem
end
end
class AppDelegate
attr_accessor :window
def setupMenu
MenuHelper.create("FooApp") do
item 'NSAlertMenu' do |sender|
alert = NSAlert.new
alert.messageText = 'This is MacRuby Status Bar Application'
alert.informativeText = 'Cool, huh?'
alert.alertStyle = NSInformationalAlertStyle
alert.addButtonWithTitle("Yeah!")
response = alert.runModal
end
item 'Open URL' do |sender|
url = NSURL.URLWithString("http://www.stackoverflow.com/");
if !NSWorkspace.sharedWorkspace.openURL(url)
puts("Failed to open url: #{url.description}");
end
end
nest 'Project' do
task1 = item 'Task1' do |sender|
puts "Task1"
p sender
end
item 'Task2' do |sender|
task1.enabled = !task1.isEnabled
end
nest 'Task3' do
item 'Subtask 1' do
puts "Task3/Subtask 1"
end
end
end
item 'Notification Center' do |sender|
notify = NSUserNotification.alloc.init
notify.title = 'Title of notification'
notify.informativeText = 'Body of notification'
NSUserNotificationCenter.defaultUserNotificationCenter.deliverNotification(notify)
end
separator
item 'Quit' do |sender|
app = NSApplication.sharedApplication
app.terminate(self)
end
end
end
def initStatusBar(menu)
status_bar = NSStatusBar.systemStatusBar
status_item = status_bar.statusItemWithLength(NSVariableStatusItemLength)
status_item.setMenu menu
img = NSImage.imageNamed 'icon.png'
status_item.setImage(img)
# status_item.title = "StatusBarSample!"
status_item.highlightMode = true
end
def applicationDidFinishLaunching(a_notification)
app = NSApplication.sharedApplication
initStatusBar(setupMenu())
puts "Your Application code here."
app.run
end
end
【追記】
s/ステータスバー/メニューバー/g thanks to kazuhooku
【追記】
s/メニューバー/メニューバーのステータスメニュー/g thanks to kazuhooku