Xcode上で記述するCocoa AppleScriptアプリケーションで、通常のWindow表示とステータスバーから呼び出すメニュー内に表示するスタイルの切り替えテストプログラムです。
macOS 10.14.6上でXcode 11.3.1を動かして確認しています。
–> Download toggleWindow.zip (Xcode Project Archive)
この機能は実際に使いたいと思っていたものの、なかなかうまく実装できていませんでした。
個人的に、GUIベースのアプリケーションを作っていると、環境設定まわりの機能を実際に組む段階でやたらと時間がかかって大変でした。
アプリケーションのメインウィンドウ上に表示している部品を環境設定ウィンドウにも同様に出す必要が出てくるわけで、その場合同じものを2度作ることは避けたいところです。通常表示している部品と、環境設定ウィンドウで表示する部品は同じものを使いまわしたいところです。
それをどのように共通化してGUI部品のかたまりを「使い回す」かについては、頭の痛い問題でした。なかなか過去形で表現するのがはばかられる状況ですが(未解決)、ここをクリアしないことには話になりません。
本テストプログラムでは、Window上に表示するビュー(NSView)とメニュー内に表示するビューに同じものを指定しています。これで、複数の場所に同じビューを使いまわしやすくなるだろうか、というところです。
NeXTstepにティアオフ・メニューという、メニューをドラッグ&ドロップするとメニューから切り離してフローティングパレットのように運用できる機能がありました。Macのアプリケーションでもこれを実装したものを見たことはありますが、使い勝手がいいかと言われると「それほどでも」といった印象でした
途中、NSMenuを閉じる処理で行き詰まっていたところ、Twitter上で@masakihoriさんに解決策を教えていただきました(aMenu’s cancelTracking())。ありがとうございます。
実際に、Window表示されているViewをメニューに入れてみたところ、いろいろ挙動が変わってくることを確認できました。ポップアップメニューについては操作できず、NSTabViewを配置して切り替えてみても、切り替えたことが内部のビューに通知されない様子。メニューに入れて使うべきではないと思われました。
テキストビューも、一応入力はできるもののコピー&ペーストなどの操作は行えません。もともと、メニューにテキストビューが入っていると違和感がすごいので、するべきではないでしょう。
セグメントビューについてはまともに使えますし、スライダーもまともに動作します。試してはいませんが、Date Picker(カレンダー表示)あたりが限界ではないでしょうか。WebViewもメニューに入れられると思いますが、どの程度インタラクティブに操作できるかについては未確認です。
本サンプルについて、当初はステータスバー上のステータスアイテムからPopoverを表示させようかと考えたのですが、menu bar extra(ステータスアイテム)からPopoverは表示できないんですね(Uni Detectorのときに実験してうまく行かず、github上で公開している人のコードも眺めてみましたが……上から下まで全部自前で作っているようでした)。
そのうえ、Appleからも「menu bar extra(ステータスアイテム)からPopoverを表示するんじゃねえ」と釘を刺されているとのことで、ステータスアイテムからメニューを表示、その中にカスタムビューを入れてみた次第です。
AppleScript名:AppDelegate.applescript |
— — AppDelegate.applescript — Toggle Window test — — Created by Takaaki Naganoya on 2020/10/26. — Copyright © 2020 Takaaki Naganoya. All rights reserved. — script AppDelegate property parent : class "NSObject" — IBOutlets property theWindow : missing value property theView : missing value property aButton : missing value property aFlag : true property aStatusItem : missing value property aMenu : missing value property bMenuItem : missing value on applicationWillFinishLaunching:aNotification aButton’s setTitle:"–> Status Menu" set aFlag to true theWindow’s setContentView:theView end applicationWillFinishLaunching: on applicationShouldTerminate:sender — Insert code here to do any housekeeping before your application quits return current application’s NSTerminateNow end applicationShouldTerminate: on clicked:aSender set aTag to (tag of aSender) as integer if aTag = 100 then if aFlag = true then –通常Window表示→Menu表示 my makeStatusItem() theWindow’s performClose:aSender aButton’s setTitle:"–> Window" else –Popup表示→通常Window表示 bMenuItem’s setHidden:true theView’s setHidden:true aMenu’s cancelTracking() theWindow’s setContentView:theView theView’s setHidden:false theWindow’s makeKeyAndOrderFront:aSender current application’s NSStatusBar’s systemStatusBar()’s removeStatusItem:aStatusItem aButton’s setTitle:"–> Status Menu" –自分を最前面に current application’s NSApp’s activateIgnoringOtherApps:true end if set aFlag to not aFlag –Flip Flap else if aTag = 110 then display notification "Clicked 1" else if aTag = 120 then display notification "Clicked 2" else if aTag = 130 then display notification "Clicked 3" end if end clicked: on actionHandler:aSender –Do nothing end actionHandler: on makeStatusItem() –if aStatusItem = missing value then return set aStatusItem to current application’s NSStatusBar’s systemStatusBar()’s statusItemWithLength:(current application’s NSVariableStatusItemLength) aStatusItem’s setTitle:"🍎" aStatusItem’s setHighlightMode:true aStatusItem’s setDelegate:me aStatusItem’s setAction:"statusclicked:" set aMenu to current application’s NSMenu’s alloc()’s init() set bMenuItem to (current application’s NSMenuItem’s alloc()’s initWithTitle:"" action:"actionHandler:" keyEquivalent:"") bMenuItem’s setView:theView (bMenuItem’s setTarget:me) (aMenu’s addItem:bMenuItem) aStatusItem’s setMenu:aMenu end makeStatusItem end script |
Dean says:
Just FYI the menu popup “Item 1”, “item 2”, “Item 3” is not working when status bar item using OS 10.14.6
Takaaki Naganoya says:
It’s a kind of limitation of gui elements placed on menu. We can’t display popup menu on the other menu item.
So, it is a normal act for every users.
We’d better to avoid to use popup menu on menu. That’s all.