自分で作成したNSStatusItemを点滅させるAppleScriptです。
ステータスバーアイテムを動的に作成して、メニューを表示して簡易メニュー的なユーザーインタフェースをAppleScriptで作成することは、割とあります。
Xcode上で作成したCocoa AppleScriptアプリケーションでも、プログラムでステータスアイテムを作成することはあります。
とくに、Window表示とステータスバーのメニュー表示を切り替えた場合など、ステータスバーアイテムを点滅させて「表示を切り替えた」ことを表現したいケースがあります。
本件は、例によって前例が見つからず、自前でいろいろ調べていたのですが、わかってしまえば簡単でした。AppleScriptで前例が見つからないのは別にそんなもんですが、Objective-Cでも見つからないのはどうかと思います。
NSStatusItemからbuttonを取得でき、これにsetWantsLayer:trueを実行すると普通にCoreAnimationでアニメーション表示させることができました。
本Scriptはスクリプトエディタ、Script Debuggerなど動作環境をとくに選ぶことなく動作します。たぶん、Switch Controlから呼び出したAppleScriptでも動作するはずです。
自分は点滅させるぐらいで満足していますが、その他のアニメーションエフェクトもいろいろ試してみました。みましたが……メニューバーという狭い領域に表示する関係上、「表示させても目立たないアニメーション」(拡大縮小など)や、「そもそも指定してもアニメーションしないアニメーション」なども見られます。
本サンプルは絵文字を表示していますが、これはあくまで「タイトル」を点滅させているものです。別途、ステータスバーアイテムに「アイコン」を指定している場合でも問題なく点滅アニメーションの表示を行えています。
AppleScript名:ステータスバーアイテムの点滅.scptd |
— Created 2017-03-03 by Takaaki Naganoya — Modified 2018-02-15 by Shane Stanley–Thanks!! — Modified 2018-02-15 by Takaaki Naganoya — Modified 2020-11-04 by Takaaki Naganoya use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" property aStatusItem : missing value on run my performSelectorOnMainThread:"init:" withObject:(missing value) waitUntilDone:true end run on init:aSender set aList to {"Piyomaru", "Software", "", "Takaaki", {"Yes", "No"}, "", "Machine", {"MacBook Pro", "MacBook Air", "Mac mini"}, "", "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) –Blink Status Bar Item set aButton to aStatusItem’s button() aButton’s setWantsLayer:true my blinkObject:aButton withRepeat:10 withDuration:1.0 –OK –my scaleObject:aButton withRepeat:10 withDuration:0.5 –OK –my rotateObject:aButton forAxis:90 withRepeat:10 withDuration:0.5 –NG –my moveObject:aButton withRepeat:10 withDuration:0.5–NG end init: on createMenu(aList) set aMenu to current application’s NSMenu’s alloc()’s init() set aCount to 10 set prevMenuItem to "" repeat with i in aList set j to contents of i set aClass to (class of j) as string if j is equal to "" then set aMenuItem to (current application’s NSMenuItem’s separatorItem()) (aMenu’s addItem:aMenuItem) else if (aClass = "text") or (aClass = "string") then if j = "Quit" 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 alloc()’s initWithTitle:j action:"actionHandler:" keyEquivalent:"") end if (aMenuItem’s setTag:aCount) (aMenuItem’s setTarget:me) (aMenu’s addItem:aMenuItem) set aCount to aCount + 10 copy aMenuItem to prevMenuItem else if aClass = "list" then –Generate Submenu set subMenu to current application’s NSMenu’s new() (aMenuItem’s setSubmenu:subMenu) set subCounter to 1 repeat with ii in j set jj to contents of ii set subMenuItem1 to (current application’s NSMenuItem’s alloc()’s initWithTitle:jj action:"actionHandler:" keyEquivalent:"") (subMenuItem1’s setTarget:me) (subMenuItem1’s setTag:(aCount + subCounter)) (subMenu’s addItem:subMenuItem1) set subCounter to subCounter + 1 end repeat end if end if end repeat return aMenu end createMenu on actionHandler:sender set aTag to tag of sender as string set aTitle to title of sender as string if aTitle is equal to "Quit" then current application’s NSStatusBar’s systemStatusBar()’s removeStatusItem:aStatusItem else display notification (aTag as string) end if end actionHandler: on blinkObject:aObject withRepeat:aTimes withDuration:durationSec set animation to current application’s CABasicAnimation’s animationWithKeyPath:"opacity" animation’s setDuration:durationSec –0.1 animation’s setAutoreverses:true animation’s setRepeatCount:aTimes animation’s setFromValue:(current application’s NSNumber’s numberWithFloat:1.0) animation’s setToValue:(current application’s NSNumber’s numberWithFloat:0.0) aObject’s layer()’s addAnimation:animation forKey:"blink" end blinkObject:withRepeat:withDuration: on scaleObject:aObject withRepeat:aTimes withDuration:durationSec set animation to current application’s CABasicAnimation’s animationWithKeyPath:"transform.scale" animation’s setDuration:durationSec animation’s setAutoreverses:true animation’s setRepeatCount:aTimes animation’s setFromValue:(current application’s NSNumber’s numberWithFloat:1.0) animation’s setToValue:(current application’s NSNumber’s numberWithFloat:2.0) aObject’s layer()’s addAnimation:animation forKey:"scale-layer" end scaleObject:withRepeat:withDuration: on rotateObject:aObject forAxis:anAxis withRepeat:aTimes withDuration:durationSec set animation to current application’s CABasicAnimation’s animationWithKeyPath:("transform.rotation." & anAxis) animation’s setDuration:durationSec animation’s setAutoreverses:true animation’s setRepeatCount:aTimes animation’s setFromValue:(current application’s NSNumber’s numberWithFloat:0.0) animation’s setToValue:(current application’s NSNumber’s numberWithFloat:4.0 * 3.1415926) aObject’s layer()’s addAnimation:animation forKey:"rotate-layer" end rotateObject:forAxis:withRepeat:withDuration: on moveObject:aObject withRepeat:aTimes withDuration:durationSec set animation to current application’s CABasicAnimation’s animationWithKeyPath:"position" animation’s setDuration:durationSec animation’s setAutoreverses:false animation’s setRepeatCount:aTimes animation’s setFromValue:(current application’s NSValue’s valueWithCGPoint:(aObject’s layer()’s position())) animation’s setToValue:(current application’s NSValue’s valueWithCGPoint:(current application’s CGPointMake(320, 480))) aObject’s layer()’s addAnimation:animation forKey:"move-layer" end moveObject:withRepeat:withDuration: |