AppleScriptでダイナミックに(動的に)生成したアラートダイアログ上にテキストビューを生成し、そのテキストビュー上にテキスト入力用のキャレットを移動させる(フォーカスする)AppleScriptです。
Xcode上でAppleScriptでGUIアプリケーションを作っていて、文字サイズの大きなテキスト入力フィールドを動的に生成。さらに、テキスト入力フィールドにテキスト入力用のキャレットを移動させようとして、割と手こずっていました。
テキスト入力を受け付けるGUI部品に対して、入力フォーカスを与え、テキスト入力のキャレットが置かれた状態を再現するには、GUIの画面上からはマウスカーソルでクリック操作するとできます。同じ状態をプログラム側から作る場合にはFirstResponderにセットするという処理になります。
この、FirstResponderに指定することは理解でき、Xcode(Interface Builder)上で配置した部品に対して実行する処理は昔からやっています。NSTextFieldに強制的に入力フォーカスを置いた上でバーコードリーダーからのテキスト入力を受け付けるといった処理です。
一方、プログラムから動的に生成したGUI部品に対して、Cocoaが用意しているAPIのどれを用いて、どのように指定すべきなのかで困りました。
親WindowのFirstResponderにしてみたり、アラートウィンドウのFirstResponderにしてみたり、いろいろ試した末に、
parentWin's setInitialFirstResponder:aView
で、Alert DialogのWindowをparentWinに、alert dialog上のテキストビューをaViewに代入した状態で実行して、入力キャレットをテキストビュー上に移動できました。本Xcode Projectは上記の1行の記述の検証を行うためのテストプロジェクトです。
AppleScript名:AppDelegate.applescript |
— — AppDelegate.applescript — dialogCarret — — Created by Takaaki Naganoya on 2020/04/14. — Copyright © 2020 Takaaki Naganoya. All rights reserved. — script AppDelegate property parent : class "NSObject" property |NSURL| : a reference to current application’s |NSURL| property NSFont : a reference to current application’s NSFont property NSView : a reference to current application’s NSView property NSAlert : a reference to current application’s NSAlert property NSColor : a reference to current application’s NSColor property NSTextView : a reference to current application’s NSTextView property NSScrollView : a reference to current application’s NSScrollView property NSRunningApplication : a reference to current application’s NSRunningApplication — IBOutlets property theWindow : missing value property returnCode : 0 property resStr : "" on applicationWillFinishLaunching:aNotification — end applicationWillFinishLaunching: on applicationShouldTerminate:sender return current application’s NSTerminateNow end applicationShouldTerminate: on clicked:sender set tmpStr to "TEST" set paramObj to {myMessage:"Input Text", mySubMessage:"", mes1:(tmpStr), mesWidth:400, mesHeight:200} my performSelectorOnMainThread:"dispTextViewWithAlertdialog:" withObject:paramObj waitUntilDone:true set aResText to (my resStr) as string display dialog aResText end clicked: on dispTextViewWithAlertdialog:paramObj –Receive Parameters set aMainMes to (myMessage of paramObj) as string –Main Message set aSubMes to (mySubMessage of paramObj) as string –Sub Message set mesStr to (mes1 of paramObj) as string –Text Input field 1 Label set aWidth to (mesWidth of paramObj) as integer –TextView width set aHeight to (mesHeight of paramObj) as integer –TextView height set vNum to system attribute "sys2" if vNum > 13 then set apRes to retLightOrDark() of me if apRes = true then set textColor to (current application’s NSColor’s cyanColor()) else set textColor to (current application’s NSColor’s blackColor()) end if else set textColor to (current application’s NSColor’s blackColor()) end if — Create a TextView with Scroll View set aScroll to NSScrollView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) set aView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) aView’s setDelegate:me aView’s setRichText:true aView’s useAllLigatures:true aView’s setTextColor:textColor aView’s setFont:(current application’s NSFont’s systemFontOfSize:90.0) set aColor to current application’s NSColor’s colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.1 aView’s setBackgroundColor:aColor aView’s setString:mesStr aScroll’s setDocumentView:aView aView’s enclosingScrollView()’s setHasVerticalScroller:true — set up alert set theAlert to NSAlert’s alloc()’s init() tell theAlert its setMessageText:aMainMes its setInformativeText:aSubMes its addButtonWithTitle:"OK" –its addButtonWithTitle:"Cancel" its setAccessoryView:aScroll set parentWin to its |window|() end tell parentWin’s setAlphaValue:0.8 –parentWin’s setOpaque:(false) parentWin’s setLevel:(current application’s NSScreenSaverWindowLevel) parentWin’s setInitialFirstResponder:aView —Place Input Carret to Alert Dialog — show alert in modal loop NSRunningApplication’s currentApplication()’s activateWithOptions:0 my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true if (my returnCode as number) = 1001 then — else set my resStr to aView’s |string|() end if end dispTextViewWithAlertdialog: on doModal:aParam set (my returnCode) to aParam’s runModal() end doModal: on retLightOrDark() return true end retLightOrDark end script |