GUIアプリを作ったときに、ステータスメニュー上に置いたアイコン>メニューからアプリ本体のウィンドウをアクティブにできないという問題が発生しています。
macOS 13、14、15とずーーっとこの動作なので、全世界的に困らされている状況があって……久しぶりにXcodeでGUIアプリをAppleScriptで作って、この問題に例外なく自分も悩まされていました。
その解決を行ったAppleScriptプロジェクトです。macOS 13.7.1上でXcode 15.2で試しています。
# 本記事投稿後にプログラムの見直しを行い、掲載リストおよびXcodeプロジェクトの差し替えを行いました。
GUIアプリ内から生成したステータスバー上のステータスアイテム。ステータスアイテムからメニュー表示。表示されたメニューから項目選択でメインウィンドウを最前面に表示する。
tell current application to activate
では何も起こりません(macOS 12まではこれで大丈夫だった)。
そういう操作を行なってもメインウィンドウが最前面に表示されず、メインウィンドウを強制的にアクティブにするようなコードを入れても、「UIがないプロセスから他のプロセスをアクティブにできない」といったワーニングが表示されるだけで、何も起こりません。
メチャクチャ困ります。
そこで、edama2さんに相談していろいろ試してみたところ、強制的にGUIアプリを最前面に持っていけました。
こちらの議論を参考にしています。
on activateMe:aSender
theWindow's makeKeyAndOrderFront:(me)
theWindow's orderFrontRegardless()
end activateMe:
当初、ウィンドウの生成を動的に行なっていましたが、その部分を削除しても動作することを確認できたため、削除しています。
なお、macOS 15.2+Xcode 16.1でビルドして実行してみたら、「tell current application to activate」だけでウィンドウを最前面に持っていけています。ここで紹介している処理は、macOS 13、14で同様の処理ができるように必要になってしまうようです。
macOS 13/14(+15.0〜15.1?)のバグと言って差し支えないでしょう。
AppleScript名:AppDelegate.applescript |
— — AppDelegate.applescript — process Control test — — Created by Takaaki Naganoya2 on 2024/11/01. — — script AppDelegate property parent : class "NSObject" — IBOutlets property theWindow : missing value property NSMenu : a reference to current application’s NSMenu property NSMenuItem : a reference to current application’s NSMenuItem property NSStatusBar : a reference to current application’s NSStatusBar property NSVariableStatusItemLength : a reference to current application’s NSVariableStatusItemLength on applicationWillFinishLaunching:aNotification my initMenu:me end applicationWillFinishLaunching: on applicationShouldTerminate:sender return current application’s NSTerminateNow end applicationShouldTerminate: on clicked:aSender my activateMe:(me) set aTag to (tag of aSender) as integer –Important!! set aTitle to (title of aSender) as string if aTag is in {41, 42, 43, 44, 45, 46} then my activateMe:aSender else if aTag = 100 then display dialog "Clicked" end if if aTitle = "Quit" then quit end clicked: on activateMe:aSender current application’s NSApp’s activateIgnoringOtherApps:(true) theWindow’s makeKeyAndOrderFront:(me) theWindow’s orderFrontRegardless() end activateMe: –Status Menu Structure on initMenu:aSender set aList to {"Piyomaru", "Software", "", "Function", {"1", "2", "3", "4", "5"}, "", "Quit"} set aStatusItem to current application’s NSStatusBar’s systemStatusBar()’s statusItemWithLength:(current application’s NSVariableStatusItemLength) aStatusItem’s setTitle:" TEST " aStatusItem’s setHighlightMode:true aStatusItem’s setMenu:(createMenu(aList) of me) end initMenu: 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:"clicked:" keyEquivalent:"") else set aMenuItem to (current application’s NSMenuItem’s alloc()’s initWithTitle:j action:"clicked:" 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:"clicked:" 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 end script |