MacRuby でメニューバーのステータスメニューに常駐するアプリを作るための雛形をつくりました!
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