Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

カテゴリー: GUI

ステータスメニューからアプリ本体を最前面に(改)

Posted on 11月 6 by Takaaki Naganoya

GUIアプリを作ったときに、ステータスメニュー上に置いたアイコン>メニューからアプリ本体のウィンドウをアクティブにできないという問題が発生しています。

macOS 13、14、15とずーーっとこの動作なので、全世界的に困らされている状況があって……久しぶりにXcodeでGUIアプリをAppleScriptで作って、この問題に例外なく自分も悩まされていました。

その解決を行ったAppleScriptプロジェクトです。macOS 13.7.1上でXcode 15.2で試しています。

# 本記事投稿後にプログラムの見直しを行い、掲載リストおよびXcodeプロジェクトの差し替えを行いました。

–> Download Xcode Projet

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

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode Bug GUI | Leave a comment

AppleScriptでMyriad Tables Libが使えないケース

Posted on 7月 28 by Takaaki Naganoya

Myriad Tables Lib(最新版はv1.13)はShane StanleyによるAppleScriptライブラリで、かつてのClassic MacOSに存在していたGUI記述系のOSAX(名前は忘れた)を思わせるほど柔軟性が高いAppleScriptライブラリの逸品です。

自分としては、これを用いる場合でも過度な作り込みを行わず、編集可能なデータビューワー的な機能を手軽に追加するのがよいと感じています。

自分でもさらに機能を簡略化した「display table by list」ライブラリを用意しています。

macOS自体のセキュリティ制限により使えないケースも

ところが、本ライブラリが機能しないケースが確認されました。

特定の呼び方を行うと、ダイアログ+表の表示そのものが封じられるという、macOS側の新たな制限によるもののようです。

そのため、自分がmacOS 10.12用にAppleScriptで開発したシステムが、現在のmacOS 14.xで部分的に動かなくなってしまいました。目下、システムを改修中です。Myriad Tables Libを使って手抜きをしていた箇所を、すべて自力で表UI(NSTableView)を使って表示・編集する必要があるようです(けっこう大変)。

ステータスメニューから呼び出した場合にブロック

同システムはステータスメニューからタイマー機能を呼び出すようなシステムだったのですが、このメニューからMyriad Tables Libを呼び出すと、Xcode上で作成したAppleScriptアプリであっても、Script Debugger上で実行したAppleScriptであっても、表示がブロックされてしまいます。

ランタイム環境がどれであっても、ステータスメニューから呼び出してMyriad Tables Libによるテーブル表示は行えませんでした。

macOS 10.12:可能
macOS 10.13:不可
macOS 10.14:(未検証)
macOS 10.15:(未検証)
macOS 11.x:(未検証)
macOS 12.x:(未検証)
macOS 13.6:不可
macOS 14.6:不可
macOS 15β:(未検証)

これは、Script Debugger上で実行したAppleScriptから、ステータスメニュー上に動的に項目を作成し、そこにメニューを追加した場合でも、Xcode上で作成した場合でも同様の挙動が見られます。

Shaneに聞いてみた

その後、Shaneに実際に聞いてみたところ、ダイアログ表示をメインスレッド実行しないとできないらしい(意訳)ことが明らかになってきました。

ダメ元で表UIつきダイアログ表示を明示的にメインスレッド指定して実行してみたところ、ハネられました。

自作の「display table by list」ライブラリを同様に呼び出してみたところ、これも表示できませんでした。このライブラリは自分が作ったのでソースコードをすべて自分が管理しており、アラートダイアログの表示ハンドラをメインスレッド指定で呼び出しています。どの時点でメインスレッド指定実行しないとダメなのかは明確ではありませんが、割と八方ふさがりな状況です。

Posted in GUI Script Libraries Security | Tagged 13.0savvy 14.0savvy | Leave a comment

メキシカンハット v3a

Posted on 7月 28 by Takaaki Naganoya

メキシカンハットの描画AppleScriptのedama2さんバージョンで、さらに多色対応したものです。

–> Download mexicanHatv3a

AppleScript名:メキシカンハット v3a.scptd
— Created 2024-06-28 by Takaaki Naganoya
— Modified 2024-07-09 by Edama2
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mLib : script "calcLibAS"
use imgDispLib : script "imageDisplayLib"

on run
  my main()
end run

on main()
  (*
  set userReply to display dialog "拡大率を入力してください(1-8)" default answer "3"
  set aScale to userReply’s text returned as number
  if aScale < 1 or 8 < aScale then return beep
  *)

  
set aScale to 3
  
  
–描画
  
set listBezierPaths to {}
  
repeat 3 times
    set listBezierPaths’s end to current application’s NSBezierPath’s bezierPath()
  end repeat
  
  
–https://x.com/yone/status/1805552100400939161
  
set D to {}
  
set dR to pi / 180
  
  
repeat 255 times
    set end of D to 192
  end repeat
  
  
repeat with y from -120 to 120 by 4
    
    
if y < 0 then
      set rY to -60
    else
      set rY to 60
    end if
    
    
repeat with x from -180 to 180 by 4
      
      
if (x < -60) then
        set rX to -120
      else if (x < 60) then
        set rX to 0
      else
        set rX to 120
      end if
      
      
set rR to (x – rX) ^ 2 + (y – rY) ^ 2
      
set z to 180 * (exp (rR / -800))
      
set sX to floor (128 + x / 2 – y / 4)
      
set sY to floor (128 – y / 4 – z / 2)
      
      
if not (sX < 0 or 256 ≤ sX) then
        if not ((D’s item (sX + 1)) ≤ sY) then
          
          
set zz to (floor (z * 0.0389)) + 1
          
log result
          
set idRect to current application’s NSMakeRect(sX * aScale, sY * aScale, aScale, aScale)
          
          
if ((zz = 1) or (zz = 3) or (zz = 5) or (zz = 7)) then
            (listBezierPaths’s item 1’s appendBezierPathWithRect:idRect)
          end if
          
if ((zz = 2) or (zz = 3) or (zz ≥ 6)) then
            (listBezierPaths’s item 2’s appendBezierPathWithRect:idRect)
          end if
          
if (zz ≥ 4) then
            (listBezierPaths’s item 3’s appendBezierPathWithRect:idRect)
          end if
          
          
set (D’s item (sX + 1)) to sY
        end if
      end if
    end repeat
  end repeat
  
  
#
  
set srcWidth to 0
  
set srcHeight to 0
  
repeat with idBezierPath in listBezierPaths
    set tmpWidth to current application’s NSMaxX(idBezierPath’s |bounds|())
    
set tmpHeight to current application’s NSMaxY(idBezierPath’s |bounds|())
    
if srcWidth < tmpWidth then set srcWidth to tmpWidth
    
if srcHeight < tmpHeight then set srcHeight to tmpHeight
  end repeat
  
  
#
  
set srcSize to current application’s NSMakeSize(srcWidth, srcHeight)
  
log result
  
set srcImage to current application’s NSImage’s alloc()’s initWithSize:srcSize
  
  
set operation to current application’s NSCompositingOperationScreen
  
–>set operation to current application’s NSCompositingOperationSourceOver
  
  
repeat with num from 1 to count listBezierPaths
    
    
if num is 1 then
      set idColor to current application’s NSColor’s blueColor()
    else if num is 2 then
      set idColor to current application’s NSColor’s redColor()
    else if num is 3 then
      set idColor to current application’s NSColor’s greenColor()
    end if
    
    
tell srcImage
      lockFocus()
      
      
tell current application’s NSImage’s alloc()
        tell initWithSize_(srcSize)
          lockFocus()
          
idColor’s setFill()
          
listBezierPaths’s item num’s fill()
          
unlockFocus()
          (
its drawAtPoint:(current application’s NSZeroPoint) fromRect:(current application’s NSZeroRect) operation:operation fraction:1.0)
        end tell
      end tell
      
      
unlockFocus()
    end tell
  end repeat
  
  
  
# 反転して合成
  
set dstImage to current application’s NSImage’s alloc()’s initWithSize:srcSize
  
tell dstImage
    lockFocus()
    
    
current application’s NSColor’s blackColor()’s setFill()
    
current application’s NSRectFill(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
    
set xform to current application’s NSAffineTransform’s transform()
    
xform’s translateXBy:0.0 yBy:(its |size|()’s height)
    
xform’s scaleXBy:1.0 yBy:-1.0
    
xform’s concat()
    
    
srcImage’s drawInRect:(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
unlockFocus()
  end tell
  
  
–結果表示
  
dispImage(dstImage, "Mexican Hat") of imgDispLib
end main

★Click Here to Open This Script 

Posted in GUI | Tagged 13.0savvy 14.0savvy 15.0savvy | Leave a comment

メキシカンハット v3

Posted on 7月 28 by Takaaki Naganoya

メキシカンハットの描画AppleScriptの、edama2さんバージョンです。

–> Download mexicanHatv3

AppleScript名:メキシカンハット v3.scptd
— Created 2024-06-28 by Takaaki Naganoya
— Modified 2024-07-09 by Edama2
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mLib : script "calcLibAS"
use imgDispLib : script "imageDisplayLib"

on run
  my main()
end run

on main()
  (*
  set userReply to display dialog "拡大率を入力してください(1-8)" default answer "3"
  set aScale to userReply’s text returned as number
  if aScale < 1 or 8 < aScale then return beep
  *)

  
set aScale to 3
  
  
  
–描画
  
set idBezierPath to current application’s NSBezierPath’s bezierPath()
  
  
–https://x.com/Stosstruppe/status/1279774386396880897
  
set dT to {}
  
set dB to {}
  
  
repeat 256 times
    set dT’s end to 192
  end repeat
  
repeat 256 times
    set dB’s end to -1
  end repeat
  
  
repeat with y from -120 to 120 by 4
    
    
if y < 0 then
      set rY to -60
    else
      set rY to 60
    end if
    
    
repeat with x from -180 to 180 by 4
      
      
if (x < -60) then
        set rX to -120
      else if (x < 60) then
        set rX to 0
      else
        set rX to 120
      end if
      
      
set rR to (x – rX) ^ 2 + (y – rY) ^ 2
      
set z to 180 * (exp (rR / -800))
      
set cX to floor (128 + x / 2 – y / 4)
      
set cY to floor (128 – y / 4 – z / 2)
      
      
if not (cX < 0 or 256 ≤ cX) then
        set pSet to false
        
        
if ((dT’s item (cX + 1)) > cY) then
          set (dT’s item (cX + 1)) to cY
          
set pSet to true
        end if
        
if ((dB’s item (cX + 1)) < cY) then
          set dB’s item (cX + 1) to cY
          
set pSet to true
        end if
        
        
if pSet then
          set idRect to current application’s NSMakeRect(cX * aScale, cY * aScale, aScale, aScale)
          (
idBezierPath’s appendBezierPathWithRect:idRect)
        end if
        
      end if
    end repeat
  end repeat
  
  
log idBezierPath’s |bounds|()
  
set srcWidth to current application’s NSMaxX(idBezierPath’s |bounds|())
  
set srcHeight to current application’s NSMaxY(idBezierPath’s |bounds|())
  
set srcSize to current application’s NSMakeSize(srcWidth, srcHeight)
  
log result
  
  
–set idColor to current application’s NSColor’s blueColor()
  
–set idColor to current application’s NSColor’s redColor()
  
set idColor to current application’s NSColor’s greenColor()
  
  
tell current application’s NSImage’s alloc()
    tell initWithSize_(srcSize)
      lockFocus()
      
idColor’s setFill()
      
idBezierPath’s fill()
      
unlockFocus()
      
set srcImage to it
    end tell
  end tell
  
  
# 反転して合成
  
set dstImage to current application’s NSImage’s alloc()’s initWithSize:srcSize
  
tell dstImage
    lockFocus()
    
    
current application’s NSColor’s blackColor()’s setFill()
    
current application’s NSRectFill(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
    
set xform to current application’s NSAffineTransform’s transform()
    
xform’s translateXBy:0.0 yBy:(its |size|()’s height)
    
xform’s scaleXBy:1.0 yBy:-1.0
    
xform’s concat()
    
    
srcImage’s drawInRect:(current application’s NSMakeRect(0, 0, its |size|()’s width, its |size|()’s height))
    
unlockFocus()
  end tell
  
  
–結果表示
  
dispImage(dstImage, "Mexican Hat") of imgDispLib
end main

★Click Here to Open This Script 

Posted in GUI | Tagged 13.0savvy 14.0savvy 15.0savvy | Leave a comment

display drop dialogライブラリをv1.5にアップデート

Posted on 11月 4, 2023 by Takaaki Naganoya

電子書籍「AppleScript最新リファレンスv2.8対応」の添付Scriptの中にこのAppleScriptライブラリを呼び出しているものがあったので、macOS 11以降のUIに応じて各種サイズを変更した「display drop dialog」のバージョン1.5をリリースしました。

複数のUTIを指定して、複数タイプの書類のドラッグ&ドロップを受け付けるようにしました。.scptと.scptdのドラッグ&ドロップを受け付けるとか。

また、本ライブラリではダイアログ上でのファイルの並び順もドラッグ&ドロップで変更できるため、複数のPDFをドラッグ&ドロップで指定して、処理順をダイアログ上で変更するといった用途に用いることを想定しています。

AppleScriptをmacOS標準搭載のスクリプトメニューから呼び出したような場合に、処理対象のファイルをドラッグ&ドロップで指定する場合のファイル受け付けのインタフェースとして用意したものです。

–> Download displayDropDialog15(Install to ~/Libraries/Script Libraries/ folder)

AppleScript名:accept AppleScript documents.scpt
use dropLib : script "display drop dialog" version "1.5"

set aMainMes to "Drop AppleScript"
set aSubMes to "Drag and Drop AppleScript files to Table (.scpt & .scptd)"
set aUTI to {"com.apple.applescript.script-bundle", "com.apple.applescript.script"}
set execButtonTitle to "Execute"

set aRes to (display drop dialog aUTI main message aMainMes sub message aSubMes with initial folder "" OK button title execButtonTitle)

★Click Here to Open This Script 

Posted in dialog file File path GUI Library | Tagged 11.0savvy 12.0savvy 13.0savvy 14.0savvy | Leave a comment

Dockアイコンにプログレスバーを追加 v3

Posted on 2月 17, 2023 by Takaaki Naganoya

実行プログラムのDock上のアイコンにプログレスバーを描画して、プログレスバーの内容をアニメーションするAppleScriptです。オリジナルはedama2さんが書かれたものです。

以前のバージョンはmacOS 10.12以前の時代に書かれたものだったので、macOS 10.13以降の形式に書き換えました。

実行プログラムが何であっても、Dockにアイコンが表示されるかぎりはアイコンにプログレスバーを描画します。スクリプトエディタ、Script Debugger、AppleScriptアプレット、Script DebuggerのEnhanced Appletなどなど。

ただし、Dockにアイコンが表示されない種類のAppleScript実行プログラム(例:スクリプトメニュー など)については、プログレスバーは表示されません。

choose colorコマンドで色選択しているのは、あくまでもデモ動作のためであり、あらかじめRGB値を指定しておけばその色でプログレスバーを描画します。

プログラム中でプログレスバーの表示ループ中にてdelayコマンドで時間待ちしているのは、目で見てわかるようにしているだけで、本来この数値はもっと小さい値でかまいません(ただし、表示のためにdelayコマンドは必須です)。

AppleScript名:Dockアイコンにプログレスバーを追加 v3.scptd
use AppleScript
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSApp : a reference to current application’s NSApp
property NSColor : a reference to current application’s NSColor
property NSImage : a reference to current application’s NSImage
property NSGradient : a reference to current application’s NSGradient
property NSZeroPoint : a reference to current application’s NSZeroPoint
property NSBezierPath : a reference to current application’s NSBezierPath

on run
  set aASCol to choose color
  
set aCocoaList to retCocoaColorList(aASCol, 65535) of me
  
set aCol to makeNSColorFromRGBAList(aCocoaList)
  
  
set max to 100
  
repeat with num from 1 to max
    my progDockTile(max, num, aCol)
    
delay 0.05
  end repeat
  
  
#アイコンを元に戻す
  
NSApp’s setApplicationIconImage:(NSImage’s imageNamed:"NSApplicationIcon")
end run

#Dockアイコンにプログレスバーを追加
on progDockTile(max, current, strartColor)
  set appIcon to NSImage’s imageNamed:"NSApplicationIcon"
  
set iconSize to appIcon’s |size|()
  
  
tell (NSImage’s alloc()’s initWithSize:iconSize)
    
    
lockFocus()
    
    
appIcon’s dissolveToPoint:(NSZeroPoint) fraction:1.0
    
set n to (iconSize’s width) / 16
    
    
#プログレスバーの長方形
    
set myRect to current application’s NSMakeRect(n / 2, n, n * 15, n * 1.6) –>{origin:{x:4.0, y:8.0}, |size|:{width:120.0, height:12.800000190735}}
    
    
tell (NSBezierPath’s ¬
      bezierPathWithRoundedRect:myRect ¬
        xRadius:(myRect’s item 2’s item 2) / 2 ¬
        
yRadius:(myRect’s item 2’s item 2) / 2)
      
      (
NSColor’s colorWithWhite:1.0 alpha:0.4)’s |set|() –>背景色
      
fill()
      
      
NSColor’s whiteColor()’s |set|() –>枠色
      
stroke()
    end tell
    
    
if current is greater than 0 then
      if current is greater than max then set current to max
      
set myRect’s item 2’s item 1 to (myRect’s item 2’s item 1) / max * current
      
      
tell (NSBezierPath’s ¬
        bezierPathWithRoundedRect:myRect ¬
          xRadius:(myRect’s item 2’s item 2) / 2 ¬
          
yRadius:(myRect’s item 2’s item 2) / 2)
        
        
–set strartColor to NSColor’s colorWithRed:0.15 green:0.55 blue:1 alpha:0.8
        
set endColor to strartColor’s shadowWithLevel:0.7
        
set grad to NSGradient’s alloc()’s initWithStartingColor:strartColor endingColor:endColor
        
grad’s drawInBezierPath:it angle:270.0
      end tell
    end if
    
    
unlockFocus()
    
    
NSApp’s setApplicationIconImage:it
  end tell
  
  
return (current + 1)
end progDockTile

–Convert "choose color" RGB list (0-65535) to Cocoa color RGBA Array (0.0-1.0)
on retCocoaColorList(aColorList, aMax)
  set cocoaColorList to {}
  
repeat with i in aColorList
    set the end of cocoaColorList to i / aMax
  end repeat
  
set the end of cocoaColorList to 1.0
  
return cocoaColorList
end retCocoaColorList

on makeNSColorFromRGBAList(colList)
  copy colList to {redValue, greenValue, blueValue, alphaValue}
  
set aColor to NSColor’s colorWithCalibratedRed:redValue green:greenValue blue:blueValue alpha:alphaValue
  
return aColor
end makeNSColorFromRGBAList

★Click Here to Open This Script 

Posted in GUI | Tagged 12.0savvy 13.0savvy Dock | Leave a comment

ステータスバーアイテムの点滅

Posted on 11月 4, 2020 by Takaaki Naganoya

自分で作成した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:

★Click Here to Open This Script 

Posted in Animation GUI | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy CABasicAnimation NSMenu NSMenuItem NSNumber NSStatusBar NSValue | Leave a comment

OSADictionaryViewの情報がない

Posted on 10月 12, 2020 by Takaaki Naganoya

macOS 10.10以降、通常のAppleScriptでもCocoaオブジェクトへのアクセスが許可されたため、macOSの中に存在するAppleScript系の機能への理解が深まりました。

正確にいえばOS X 10.6でXcode上のCocoa AppleScriptアプリケーション内でCocoaの機能が利用できたわけですが、やはり通常のAppleScriptの中で利用できると理解の度合いが違います。

そうしたCocoaなどのmacOSのAPIは数多くあり、今日ではPDF関連の処理など専用のアプリケーションを使わずにたいていの処理ができるようになり、Web上のREST APIを自在に呼び出せるようになり、各種画像フィルタ処理や機械学習までAppleScriptから呼び出せるようになりました。Cocoaの機能が呼べなかった時代に戻れと言われたら途方に暮れてしまうレベルで日常的な風景になりました。

そんなmacOS側の機能に、AppleScript系の機能が用意されています。

大きく分けると、

①Cocoa系のOSAKit。AppleScriptを実行したりOSAコンポーネント情報を取得するなどの機能。AppleScriptから日常的によく使っています。

②AppKit内のNSAppleScript。AppleScriptの実行を行うための部品と、プロセス間通信のAppleEventsまわりの機能です。このあたりはAppleScriptからは手が出ません。

③Carbon系のOpenScripting。AppleScript言語処理系そのものといってよいでしょう。まったく手が出ません。

などの機能の所在がわかっています。Xcode上から調べるとヘッダーファイルも調査できて、どのようなメソッドが揃っているかも確認できます。

これらの他に、割とメジャーそうな機能なのにヘッダーファイルも何も一切公開されていないオブジェクトがあります。

それが、AppleScript用語辞書の表示関連のOSADictionaryViewとOSADictionaryControllerです。

これについては、不思議と情報が何もありません。あたりをつけて、「おそらくPathかURLをコントローラーに渡して初期化するとViewに表示するんだろう」と試しているのですが、まったく歯が立ちません。


▲macOS 10.14.6+Xcode 11.3.1の組み合わせで、OSADictionaryViewをドラッグ&ドロップで部品配置すると、その瞬間にXcodeがクラッシュ(事前にプロジェクトにOSAKit.frameworkをinclude)。この環境にはNSBrowserなど同様の瞬間クラッシュを引き起こす部品がある。macOS 10.15上では修正されているため、10.14上での対処は行わないとAppleから明言されました

とりあえずは、いつものアラートダイアログ上でOSADictionaryViewを表示して、指定のsdefファイルを表示するレベルからはじまって、GUIアプリケーション上でSDEF表示機能を実装するところがゴール地点でしょうか。失敗を重ねながら何度も調査やテストを行うことになります。

OSADictionaryView/OSADictionaryControllerの情報があったところで、これが使い物になるのかどうかという疑問があります。

OSADictionaryViewが使えたとして、それはsdefファイルのパスなりURLを渡して表示させるようなものになると想像されますが、このsdefに外部のファイルをincludeするような記述ができるようになったことで、「対象アプリケーションが実際に起動しないとAppleScript用語辞書全体を取得することができない」という状況に。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary xmlns:xi="http://www.w3.org/2003/XInclude">
	<!-- We override some standard commands to add several optional properties. -->
	<suite name="Standard Suite" code="????" description="Common classes and commands for all applications.">
		<xi:include href="file://localhost/System/Library/ScriptingDefinitions/CocoaStandard.sdef" xpointer="xpointer(/dictionary/suite/node()[not((self::command and (@name = 'open')) or (self::class and (@name = 'window')))])"/>

▲最近のMicrosoft Officeアプリケーションでは、sdefのサイズ縮小のためかxi:includeの記述が見られる

Adobeのアプリケーションには、sdefファイルを起動後にプログラムで生成しているものもあるため、起動前の状態でsdefファイル単独でブラウズできてもあまり意味がないケースが生じてしまいます。

つまり、Viewを単体で利用できても不十分かつ部分的なAppleScript用語辞書しか表示できないのではないか? という疑問を持つわけです。


▲1:17秒あたりでToolbar上のコマンドからAppleScript用語辞書の表示を行わせています。これは、Script Editorに辞書の表示を行わせる処理です

実際、スクリプトエディタに指定アプリケーションのAppleScript用語辞書を表示するような命令は行えるので、結局はそちらを使うことになるのではないかと考えるものです。苦労して使い方を突き止めたとしても、実用性がなかったら残念なことです。そして、その可能性がきわめて高そうな気配がしているのです。

Posted in GUI OSA | Tagged 10.14savvy 10.15savvy 11.0savvy | Leave a comment

WebKit Utilities v2

Posted on 3月 16, 2020 by Takaaki Naganoya

AppleScriptで指定URLのページを表示する「WebKit Utilities」がmacOS 10.13以降の環境で動かなくなっていたので、書き換えて動くようにしておきました。

–> Download WebKit Utilities_archive v2

これ自体が役に立ったということはなく、単にAppleScript Librariesの書き方のサンプルと理解しています。Webサイトの表示を行うよりも1枚ものの画像やPDFにレンダリングする処理のほうがバッチ処理の中では相性がいいと思います。

また、このScriptはWkWebViewではなく古いWebViewを使っているので、じきに動かなくなります。

AppleScript名:WebKit Utilitiesで指定URLをウィンドウ表示
— Created 2017-03-24 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use webLib : script "WebKit Utilities"

set targetURL to "http://www.piyocast.com/"
display URL targetURL window size {1024, 1024}

★Click Here to Open This Script 

AppleScript名:WebKit Utilities
— An example AppleScript/Objective-C library that uses the WebKit framework.

use AppleScript
use framework "AppKit"
use framework "WebKit"

property NSURL : a reference to current application’s NSURL
property WebView : a reference to current application’s WebView
property NSScreen : a reference to current application’s NSScreen
property NSThread : a reference to current application’s NSThread
property NSWindow : a reference to current application’s NSWindow
property NSURLRequest : a reference to current application’s NSURLRequest
property NSMutableDictionary : a reference to current application’s NSMutableDictionary

on _DisplayWebWindowWithURL:theURLString windowSize:theWindowSize
  — Create and display a horizontally centered window with a WebView
  
  
set {thisWindowWidth, thisWindowHeight} to theWindowSize
  
set screenBounds to the NSScreen’s mainScreen’s visibleFrame as any
  
  
if class of screenBounds = record then
    set screenWidth to screenBounds’s |size|’s width
    
set screenHeight to screenBounds’s |size|’s height
    
set windowLeft to ((screenWidth – thisWindowWidth) / 2) + (screenBounds’s origin’s x)
    
set windowBottom to screenHeight – thisWindowHeight + (screenBounds’s origin’s y) – 40
  else
    copy screenBounds to {{originX, originY}, {screenWidth, screenHeight}}
    
set windowLeft to ((screenWidth – thisWindowWidth) / 2) + originX
    
set windowBottom to screenHeight – thisWindowHeight + originY – 40
  end if
  
  
set theStyleMask to (get current application’s NSTitledWindowMask) + (get current application’s NSClosableWindowMask) + (get current application’s NSMiniaturizableWindowMask) + (get current application’s NSResizableWindowMask)
  
set webWindow to NSWindow’s alloc()’s initWithContentRect:(current application’s NSMakeRect(windowLeft, windowBottom, thisWindowWidth, thisWindowHeight)) ¬
    styleMask:theStyleMask backing:(current application’s NSBackingStoreBuffered) defer:false
  — set webWindow’s title to "WebView Created with AppleScript – " & theURLString
  
  
set theWebView to WebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, thisWindowWidth, thisWindowHeight)) ¬
    frameName:"WebKit Frame" groupName:"WebKit Group"
  set webWindow’s contentView to theWebView
  
  
tell webWindow to makeKeyAndOrderFront:(missing value)
  
  
— Start loading the URL
  
  
set theURL to NSURL’s URLWithString:theURLString
  
set theURLRequest to NSURLRequest’s requestWithURL:theURL
  
tell theWebView’s mainFrame to loadRequest:theURLRequest
  
  
webWindow
end _DisplayWebWindowWithURL:windowSize:

— The following two handlers exist to deal with running on a background thread.
— If we’re on a background thread, we have to run the UI code on the main thread.

on _DisplayWebWindowMainThread:parameterDictionary
  try
    set theURLString to parameterDictionary’s objectForKey:"url string"
    
set theWindowSize to parameterDictionary’s objectForKey:"window size"
    
    
set webWindow to my _DisplayWebWindowWithURL:theURLString windowSize:theWindowSize
    
    
tell parameterDictionary to setObject:webWindow forKey:"result"
  on error errMsg number errNum
    using terms from scripting additions
      display alert "Error!" message errMsg & " (" & errNum & ")"
    end using terms from
    
tell parameterDictionary to setObject:{errMsg, errNum} forKey:"error"
  end try
end _DisplayWebWindowMainThread:

on display URL theURLString window size theWindowSize
  — All UI methods must be invoked on the main thread.
  
if (NSThread’s isMainThread) then
    my _DisplayWebWindowWithURL:theURLString windowSize:theWindowSize
  else
    — Parameters, results and error information are communicated between threads via a dictionary.
    
set parameterDictionary to NSMutableDictionary’s dictionary()
    
tell parameterDictionary to setObject:theURLString forKey:"url string"
    
tell parameterDictionary to setObject:theWindowSize forKey:"window size"
    
    
its performSelectorOnMainThread:"_DisplayWebWindowMainThread:" withObject:parameterDictionary waitUntilDone:true
    
    
— Propagate errors from the other thread
    
    
set errInfo to parameterDictionary’s objectForKey:"error"
    
if errInfo is not missing value then error errInfo’s first item number errInfo’s second item
    
    
parameterDictionary’s objectForKey:"result"
  end if
end display URL

★Click Here to Open This Script 

Posted in GUI Internet URL | Tagged 10.13savvy 10.14savvy 10.15savvy NSMutableDictionary NSScreen NSThread NSURL NSURLRequest NSWindow WebView | Leave a comment

AppleScriptでキースキャン

Posted on 2月 26, 2020 by Takaaki Naganoya

Xcode上で作成するAppleScript Cocoa Applicationで、キースキャンを試してみました。

ふだん作っているものだと、各種パラメータをGUI上で設定する程度のもので、キースキャンを行う必要などこれっぽっちもないのですが、いま作っているアプリケーションでキースキャンが必要になってしまったので、昔作ったものを引っ張り出してきました。

AppleScriptのプログラムでキースキャンを行うといえば、AppleScript Appletの起動時に何らかのModifier Keys(ShiftとかOptionとかCommandとかControlとか)が押されていることを検出して動作を変更するといった処理が一般的です。ループ処理中でも、これらのキー入力を定期的に監視することはよく行なっています(処理中に停止したいという要求はあるので)。

–> Watch Demo Movie

–> Download Xcode Project Archive

本プログラムでは、Modifier Keysにかぎらずキーボード入力全般を受け付けています。ただし、キースキャン可能なのは本プログラムが最前面にある場合のみです。

掲載しているコードからではわかりませんが、キー入力の受け付けをNSWindowで行なっています。FirstResponderまわりを一切いじくらずにほぼプログラミングなしでキー受け付けを行おうとした結果NSWindowで行うことになったというわけで、これがベストとも思いません。

とりあえず「こうすればできた」というレベルをおさえておいて、そこから自分の好きな方向に機能を変更していけばよいと思います。

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— keyEvents
—
— Created by Takaaki Naganoya on 2014/05/09.
— Copyright (c) 2014年 Takaaki Naganoya. All rights reserved.
—

script AppDelegate
  property parent : class "NSObject"
  
  
— IBOutlets
  
property theWindow : missing value
  
property aButton : missing value
  
  
property xMax : 500
  
property yMax : 500
  
  
property aStep : 50
  
  
on applicationWillFinishLaunching:aNotification
    —
  end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
  
on buttonMove:(aCode as integer)
    set curFrame to aButton’s frame()
    
copy curFrame to {{x, y}, {xWidth, yHeight}}
    
    
if aCode = 123 then
      –Left
      
if x > 0 then
        set x to x – aStep
      end if
    else if aCode = 124 then
      –Right
      
if x < xMax then
        set x to x + aStep
      end if
      
    else if aCode = 126 then
      –Up
      
if y < yMax then
        set y to y + aStep
      end if
      
    else if aCode = 125 then
      –Down
      
if y > 10 then
        set y to y – aStep
      end if
    else if aCode = 125 then
      
    end if
    
    
set newRect to {{x, y}, {xWidth, yHeight}}
    
aButton’s setFrame:newRect
    
aButton’s setNeedsDisplay()
  end buttonMove:
end script

★Click Here to Open This Script 

AppleScript名:keyEventWin.applescript
script keyEvWin
  
  
property parent : class "NSWindow"
  
  
property aButton : missing value
  
  
  
on canBecomeKeyWindow:sender
    return true
  end canBecomeKeyWindow:
  
  
on canBecomeMainWindow:sender
    return true
  end canBecomeMainWindow:
  
  
  
on keyDown:theEvent
    set aCode to (theEvent’s keyCode) as integer
    
    
if aCode = 123 then
      –左
      
current application’s NSApp’s delegate()’s performSelector:"buttonMove:" withObject:(aCode)
      
    else if aCode = 124 then
      –右
      
current application’s NSApp’s delegate()’s performSelector:"buttonMove:" withObject:(aCode)
      
    else if aCode = 126 then
      –上
      
current application’s NSApp’s delegate()’s performSelector:"buttonMove:" withObject:(aCode)
      
    else if aCode = 125 then
      –下
      
current application’s NSApp’s delegate()’s performSelector:"buttonMove:" withObject:(aCode)
      
    end if
    
    
  end keyDown:
  
end script

★Click Here to Open This Script 

Posted in AppleScript Application on Xcode GUI | Tagged 10.13savvy 10.14savvy 10.15savvy NSButton NSEvent NSWindow | Leave a comment

display dialogで押されたボタンをIDで返す(1はじまり)

Posted on 2月 23, 2020 by Takaaki Naganoya

If you can think of something that a language would normally have applescript does not have it

— mc²than (@parrotgeek1) February 23, 2020

できると思うんだけどなー、と思って30分ぐらいでできました。全部同じtitleのボタンを複数作って、押されたボタンを左側から1はじまりでIDで返すAppleScriptです。

できたところで誰が褒めてくれるわけでもありませんが、できるものをできないというのは、、、、。

そもそも、同じtitleのボタンを複数出すのはGUI的にナンセンスな上に、Xcode上でAppleScriptアプリケーション作れば普通に作れるわけで、、、、

AppleScript名:display dialogで押されたボタンをIDで返す(1はじまり).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/02/23
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property NSAlert : a reference to current application’s NSAlert
property NSRunningApplication : a reference to current application’s NSRunningApplication

property theResult : 0
property returnCode : 0
property theDataSource : {}

display dialog "TEST" buttons {"OK", "OK", "OK", "OK", "OK", "OK", "OK"}

on display dialog aString as string buttons buttonList as list
  set paramObj to {myMessage:aString, myButtonList:buttonList}
  
–my dispAlert:paramObj –for debug
  
my performSelectorOnMainThread:"dispAlert:" withObject:(paramObj) waitUntilDone:true
  
return {button returned:my theResult}
end display dialog

on dispAlert:paramObj
  set aMainMes to (myMessage of paramObj) as string
  
set bList to (myButtonList of paramObj) as list
  
set bLen to length of bList
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
    
repeat with i in bList
      (its addButtonWithTitle:i)
    end repeat
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
set aBID to bLen – ((my returnCode) – 1000 + 1) + 1
  
set my theResult to aBID
end dispAlert:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.14savvy 10.15savvy NSAlert NSRunningApplication | Leave a comment

AppleScriptでCoreAnimationを利用

Posted on 2月 13, 2020 by Takaaki Naganoya

Xcode上で作成するCocoa AppleScript Applicationにおいて、CoreAnimationを利用するサンプルProjectです。

–> Download Xcode Project Test with Xcode 11.3.1 + macOS 10.14.6

ひととおり(このぐらい)CoreAnimationでGUI部品をアニメーションするテストを行なっていました。あまり使いすぎるのは下品に見えるとの判断から、実際のアプリケーションでは最低限の地味な利用にとどめていました。

–> Watch Demo (1) Double PDF

–> Watch Demo (2) This Project

Mac App Storeで販売している100% AppleScriptで記述したアプリケーション「Double PDF 2.0」においても、コマンド実行後にメニューを更新する際、更新したメニューを点滅表示するぐらいの「節度あるお付き合い」にとどめていました。

それが、ここ最近組んでいるアプリケーションではド派手に利用する必要があるようで、再度こうした試作品を引っ張り出してテストしだしています。

AppleScript名:AppDelegate.applescript
—
— AppDelegate.applescript
— GUI Animation
—
— Created by Takaaki Naganoya on 2017/02/13.
— Copyright 2017 Takaaki Naganoya. All rights reserved.
—
— http://liu044100.blogspot.jp/2013/07/cabasicanimation.html
script AppDelegate
  property parent : class "NSObject"
  
  
— IBOutlets
  
property theWindow : missing value
  
property aPopup : missing value
  
  
property gui1 : missing value
  
property gui2 : missing value
  
property gui3 : missing value
  
property gui4 : missing value
  
property gui5 : missing value
  
property gui6 : missing value
  
property gui7 : missing value
  
property gui8 : missing value
  
property gui9 : missing value
  
property guiA : missing value
  
property guiB : missing value
  
property guiC : missing value
  
  
  
  
on applicationWillFinishLaunching:aNotification
    — Insert code here to initialize your application before any files are opened
  end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
on clicked:sender
    set aInd to aPopup’s indexOfSelectedItem()
    
set guiList to {gui1, gui2, gui3, gui4, gui5, gui6, gui7, gui8, gui9, guiA, guiB, guiC}
    
repeat with i in guiList
      if aInd = 0 then
        (my blinkObject:i)
      else if aInd = 1 then
        (my scaleObject:i)
      else if aInd = 2 then
        (my rotateObject:i forAxis:"x")
      else if aInd = 3 then
        (my rotateObject:i forAxis:"y")
      else if aInd = 4 then
        (my rotateObject:i forAxis:"z")
      else if aInd = 5 then
        (my moveObject:i)
      else if aInd = 6 then
        –my mixtureAnimeObject:i
      end if
      
delay 0.1
    end repeat
  end clicked:
  
  
  
  
on blinkObject:aObject
    set animation to current application’s CABasicAnimation’s animationWithKeyPath:"opacity"
    
animation’s setDuration:0.1
    
animation’s setAutoreverses:true
    
animation’s setRepeatCount:4
    
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:
  
  
on scaleObject:aObject
    set animation to current application’s CABasicAnimation’s animationWithKeyPath:"transform.scale"
    
animation’s setDuration:0.1
    
animation’s setAutoreverses:true
    
animation’s setRepeatCount:2
    
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:
  
  
on rotateObject:aObject forAxis:anAxis
    set animation to current application’s CABasicAnimation’s animationWithKeyPath:("transform.rotation." & anAxis)
    
animation’s setDuration:2.0
    
–animation’s setAutoreverses:false
    
animation’s setRepeatCount:1
    
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:
  
  
  
on moveObject:aObject
    set aFrame to aObject’s frame()
    
copy aFrame to {{fx1, fy1}, {fx2, fy2}}
    
set animation to current application’s CABasicAnimation’s animationWithKeyPath:"position"
    
animation’s setDuration:0.4
    
animation’s setAutoreverses:false
    
animation’s setRepeatCount:1
    
animation’s setFromValue:(current application’s NSValue’s valueWithCGRect:(aObject’s frame()))
    
animation’s setToValue:(current application’s NSValue’s valueWithCGRect:(current application’s CGRectMake(100, 100, fx2, fy2)))
    
aObject’s layer()’s addAnimation:animation forKey:"move-layer"
  end moveObject:
  
  
–Not Work Yet…..
  
(*
  on mixtureAnimeObject:aObject
    set animation1 to current application’s CABasicAnimation’s animationWithKeyPath:"transform.translation.x"
    animation1’s setToValue:(current application’s NSNumber’s numberWithFloat:80.0)
    animation1’s setDuration:3.0
    
    set animation2 to current application’s CABasicAnimation’s animationWithKeyPath:"transform.rotation.z"
    animation2’s setFromValue:(current application’s NSNumber’s numberWithFloat:0.0)
    animation2’s setToValue:(current application’s NSNumber’s numberWithFloat:4.0 * 3.1415926)
    animation2’s setDuration:3.0
    
    set aGroup to current application’s CAAnimationGroup’s animation()
    aGroup’s setDuration:3.0
    aGroup’s setRepeatCount:1.0
    aGroup’s setAnimations:(current application’s NSArray’s arrayWithObjects:{animation1, animation2, missing value})
    aObject’s layer()’s addAnimation:aGroup forKey:"move-rotate-layer"
  end mixtureAnimeObject:
*)
end script

★Click Here to Open This Script 

Posted in Animation AppleScript Application on Xcode GUI | Tagged 10.13savvy 10.14savvy 10.15savvy CABasicAnimation NSNumber | Leave a comment

Numbersで書類Aのすべてのシートの表1を、書類Bの現在のシートの表1にまとめる

Posted on 1月 21, 2020 by Takaaki Naganoya

Numbersでオープン中の複数の書類があったときに、書類Aのすべてのシートの表1を、書類Bの現在のシートの表1にまとめるAppleScriptです。

さすがに、機械的な作業すぎて手で行う気にはなれなかったので、ありもののScriptを利用して作ってみました。

こういう用途のために作っておいたライブラリ「choose multiple list」を利用しています。


▲データ取得元


▲データ集約先


▲ダイアログで「データ取得元」と「データ集約先」を選択

BridgePlus内蔵のFrameworkがmacOS 10.14/10.15に邪魔されて認識されない環境では動かせないかもしれません。このあたり、SIP解除するしかないと思わせるものがあります。

ライブラリで複雑な選択を行えるUI部品を作っておいたおかげで、これだけ込み入った動作を行うAppleScriptを書きましたが、Cocoaの機能はほぼ使っていません(getDataFromNumbersDocは作り置きしておいた、頻出ルーチンなので使いまわしています)。choose multiple listライブラリは、こういう用途のために作っておいたものであり、まさにそのぴったりな用途であったといえるでしょう。

AppleScript名:書類Aのすべてのシートの表1を、書類Bの現在のシートの表1にまとめる.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/01/20
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.7" — High Sierra (10.13) or later
use framework "Foundation"
use scripting additions
use bPlus : script "BridgePlus"
use mulList : script "choose multiple list"

script spd
  property allData : {}
end script

set (allData of spd) to {}

set {aName, bName} to getTargetDocNames() of me

–「データ取得元」のNumbers書類のすべてのシートの表1から2D Listでデータ取得
tell application "Numbers"
  tell document aName
    set sList to name of every sheet
    
repeat with i in sList
      set tmp2DDat to getDataFromNumbersDoc(aName, i) of me
      
set (allData of spd) to (allData of spd) & tmp2DDat
    end repeat
  end tell
end tell

set aHeight to length of (allData of spd)
set aWidth to length of item 1 of (allData of spd)

–「データ集約先」のNumbers書類の現在のシートの表1にまとめた2D Listを展開する(巨大すぎると時間がかかるので、CSV書き出ししてオープンするなどの別の方法を採るべき)
tell application "Numbers"
  tell document bName
    tell active sheet
      set tRes to make new table with properties {row count:aHeight + 1, column count:aWidth}
    end tell
  end tell
end tell

fillCurrentTable(bName, (allData of spd)) of me

–Numbersの書類の現在のシートを、指定の2次元配列でfillする
on fillCurrentTable(docName, aList)
  set aLen to length of aList
  
set aWidth to length of first item of aList
  
  
tell application "Numbers"
    tell document docName
      tell active sheet
        tell table 1
          repeat with i from 1 to aLen
            tell row (i + 1)
              set aRowList to contents of item i of aList
              
repeat with ii from 1 to aWidth
                tell cell ii
                  set aTmpData to contents of item ii of aRowList
                  
ignoring application responses
                    set value to aTmpData
                  end ignoring
                end tell
              end repeat
            end tell
          end repeat
        end tell
      end tell
    end tell
  end tell
end fillCurrentTable

–Numbersでオープン中の書類の名称一覧からデータ取得元とデータ集約先の書類名をダイアログで選択
on getTargetDocNames()
  tell application "Numbers"
    set nList to name of every document
  end tell
  
  
set selList to {nList, nList}
  
set tList to {"データ取得元", "データ集約先"}
  
  
set {aRes, bRes} to choose multiple list selList main message "各Numbers書類の役割を選択してください" sub message "「データ取得元」のデータを順次、「データ集約先」の表1に連結します" with title lists tList height 140 width 400 return type item contents without allow same items
  
return {aRes, bRes}
end getTargetDocNames

–Numbersでオープン中の書類の選択中のシートの表1からデータを取得して2D Listに
on getDataFromNumbersDoc(aDocName, sheetName)
  
  
load framework
  
  
tell application "Numbers"
    if (count (every document)) = 0 then return false
    
    
tell document aDocName
      if (count (every sheet)) = 0 then return false
      
      
tell sheet sheetName
        tell table 1
          set colCount to column count
          
set rowCount to row count
          
set headerCount to header row count
          
set footerCount to footer row count
          
          
set dList to value of every cell of cell range
        end tell
      end tell
      
    end tell
  end tell
  
  
–Convert 1D List to 2D List
  
set bList to (current application’s SMSForder’s subarraysFrom:dList groupedBy:colCount |error|:(missing value)) as list
  
set sItem to 1 + headerCount
  
set eItem to rowCount – footerCount
  
set cList to items sItem thru eItem of bList
  
  
return cList
  
end getDataFromNumbersDoc

★Click Here to Open This Script 

Posted in dialog GUI list | Tagged 10.13savvy 10.14savvy 10.15savvy Numbers | Leave a comment

display drop dialog Script Library

Posted on 1月 16, 2020 by Takaaki Naganoya

指定UTIのファイルのFinderからのドラッグ&ドロップを受け付けるAppleScript Libraries「display drop dialog」をリリース、フリー配布いたします。

# 本ライブラリはEdama2さんのScriptに若干の改変を行い、sdefをつけてパラメータのエラー処理を行い、コードサインしてライブラリ化したものです

–> Download display drop dialog Script Library (To ~/Library/Script Libraries/)

本ライブラリは、指定UTIの書類のドラッグ&ドロップを受け付けるダイアログをAppleScriptから手軽に使えるようにするものです。macOS 10.14以降で動作します。

現在のAppleのTim Cook体制は、セキュリティ強化の美名のもとに「動かないコンピュータ」を目指し、各種機能を阻害する方向に進んでいます。

AppleScriptの世界はその中でも自由度が比較的高い状態にありますが、それでもmacOS 10.12以降では、AppleScriptドロップレットにFinderからドラッグ&ドロップしても、Finderの拡張属性(Xattr)がついたファイルは無視されたりと、なかなか困ります。

そこで、Finderからのドラッグ&ドロップ操作を通常の(Applet書き出ししていない)AppleScript、とくにScript Menuから呼び出す形式のAppleScriptで使えるようにすることを目的として本ライブラリを作成しました。

本ライブラリを用いることで、ドロップレットを作らなくても、Script Menuから呼び出した普通のAppleScriptにファイルのドラッグ&ドロップの受信機能を追加できます。セキュリティと機能性(ドラッグ&ドロップ)を両立させるため、すべてのScripterに欠かすことのできないライブラリになるものと期待しています。

本ライブラリのAppleScript用語辞書には実行イメージ(画面キャプチャ)およびサンプルScriptを入れてあり、実行時の様子やサンプルスクリプト(本Blogと同様のワンクリックで内容が転送されるリンクつき)を確認できるようになっています。

AppleScript名:accept AppleScript documents
use dropLib : script "display drop dialog"

set aMainMes to "Drop AppleScript"
set aSubMes to "Drag and Drop AppleScript files here"
set aUTI to "com.apple.applescript.script"
set execButtonTitle to "Execute"
set aRes to (display drop dialog aUTI main message aMainMes sub message aSubMes with initial folder "" OK button title execButtonTitle)
–> {alias "Machintosh HD:Users:me:Documents:display drop dialog:accept PNG images.scpt", alias "Machintosh HD:Users: me:Documentsaccept Markdown documents.scpt"}

★Click Here to Open This Script 

AppleScript名:accept Markdown documents
use dropLib : script "display drop dialog"

set aMainMes to "Drop Markdown Documents"
set aSubMes to "Drag and Drop Markdown documents here"
set aUTI to "net.daringfireball.markdown"
set execButtonTitle to "Execute"
set aRes to (display drop dialog aUTI main message aMainMes sub message aSubMes with initial folder "" OK button title execButtonTitle)
–> {alias "Machintosh HD:Users:me:Documents::–Book 11 「Blog Archives vol5 2013-2014」:2013:01:201301013.md", alias "Machintosh HD:Users:me:Documents::–Book 11 「Blog Archives vol5 2013-2014」:2013:01:201301012.md"}

★Click Here to Open This Script 

AppleScript名:accept PNG images with initial folder contents
use scripting additions
use dropLib : script "display drop dialog"

set aMainMes to "Drop PNG"
set aSubMes to "Drag and Drop png images here"
set aUTI to "public.png"
set execButtonTitle to "Execute"
set iFolder to path to pictures folder –At first, PNG files in this folder is displayed
set aRes to (display drop dialog aUTI main message aMainMes sub message aSubMes with initial folder iFolder OK button title execButtonTitle)
–> {alias "Machintosh HD:Users:me:Pictures:1015sedhelp2.png", alias "Machintosh HD:Users:me:Pictures:574G01.png", alias "Machintosh HD:Users:me:Pictures:macDown1.png", alias "Machintosh HD:Users:me:Pictures: 2017-09-27 19.33.53.png", alias "Machintosh HD:Users:me:Pictures:2018-11-01 10.54.06.png"

★Click Here to Open This Script 

Posted in dialog GUI PRODUCTS Script Libraries sdef | Tagged 10.14savvy 10.15savvy | Leave a comment

アラートダイアログ上にTable Viewを表示 v9_アイコンを表示、Finderからのドラッグ&ドロップ

Posted on 1月 15, 2020 by Takaaki Naganoya

Edama2さんからの投稿Scriptです。アラートダイアログ中に各種GUI部品を詰め込む「箱庭インタフェース」シリーズ。テーブルビューを表示して、Finderからのアプリケーションのドラッグ&ドロップを受け付けるAppleScriptです。

–> Download Editable & Executable Script Bundle Document

カスタムクラスの作り方がわかったので、懲りずにTable Viewネタです。
Thanks  Shane Stanley!

アプリケーションフォルダ直下のアプリを、NSValueTransformerを使ってコラムにローカライズ名とアイコンを表示するのと、finderからのドラッグ&ドロップでアイテムの追加です。
追加時に重複チェックをしているので試す時はユーティリティフォルダか別の場所にあるアプリで試してください。

本Scriptは自分も作りたいと思いつつも調査が進んでいなかったものですが、TableViewでFinderからのドラッグ&ドロップを受け付けます。

これを鍛えて再利用しやすい部品に育てることで、ドロップレットの代替品にできると思っています。macOS 10.12以降、Finder上のファイルをAppleScriptドロップレットにドラッグ&ドロップで処理させても、OS側がダウンロードファイルにつけた拡張属性(Xattr)「com.apple.quarantine」がついているとドロップレット側で処理できなかったりします(回避方法はありますけれども)。

このドロップレットという仕組みを使わずに、同様にドラッグ&ドロップによる処理を手軽にできる代替インタフェースとしてこのようなものを考えていた次第です。自分で作らなくてもEdama2さんが作ってくださったので助かりました、、、、

AppleScript名:アラートダイアログ上にTable Viewを表示 v9_アイコンを表示、Finderからのドラッグ&ドロップ
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppleScriptObjC"
use scripting additions

on run
  my main()
end run
on main()
  # Script Bundle内のResourcesフォルダを求める
  
set resourcePath to POSIX path of (path to me) & "Contents/Resources/"
  
set theBundle to current application’s NSBundle’s bundleWithPath:resourcePath
  
theBundle’s loadAppleScriptObjectiveCScripts()
  
  
#
  
set aFolder to path to applications folder
  
set aPath to aFolder’s POSIX path
  
set aURL to current application’s NSURL’s fileURLWithPath:aPath
  
  
# 指定フォルダの直下のファイルを取得
  
set filePaths to current application’s NSFileManager’s defaultManager’s ¬
    contentsOfDirectoryAtURL:aURL ¬
      includingPropertiesForKeys:{current application’s NSURLNameKey} ¬
      
options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) ¬
      
|error|:(missing value)
  
  
set filePaths to filePaths as list
  
set thisFileType to "com.apple.application-bundle"
  
  
set fileURLs to {}
  
repeat with anItem in filePaths
    
    
set aPath to anItem’s contents’s POSIX path
    
set aURL to (current application’s NSURL’s fileURLWithPath:aPath)
    
    
set {aResult, aUTI, aError} to (aURL’s getResourceValue:(reference) ¬
      forKey:(current application’s NSURLTypeIdentifierKey) ¬
      
|error|:(reference))
    
    
if (aUTI as text) is thisFileType then
      set fileURLs’s end to aURL
    end if
  end repeat
  
  
set optionRec to {fileURLs:fileURLs, fileType:thisFileType}
  
  
set aMainMes to "アプリケーションの選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
set dateObj to my chooseItemByTableView(aMainMes, aSubMes, optionRec)
end main

# アラートダイアログでtableviewを表示
on chooseItemByTableView(aMainMes, aSubMes, optionRec)
  ### set up view
  
set {theView, makeObj} to my makeContentView(optionRec)
  
  
set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, setView:theView, myOption:makeObj}
  
my performSelectorOnMainThread:"raizeAlert:" withObject:paramObj waitUntilDone:true
  
  
if (my _retrieve_data) is missing value then error number -128
  
return (my _retrieve_data)
end chooseItemByTableView

## retrieve date
on raizeAlert:paramObj
  set mesText to paramObj’s myMessage
  
set infoText to paramObj’s mySubMessage
  
set theView to paramObj’s setView
  
set aTableObj to paramObj’s myOption
  
  
global _retrieve_data
  
set _retrieve_data to missing value
  
  
### set up alert
  
tell current application’s NSAlert’s new()
    setMessageText_(mesText)
    
setInformativeText_(infoText)
    
addButtonWithTitle_("OK")
    
addButtonWithTitle_("Cancel")
    
setAccessoryView_(theView)
    
tell |window|()
      setInitialFirstResponder_(theView)
    end tell
    
#### show alert in modal loop
    
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
  end tell
  
  
### retrieve date
  
set aIndexSet to aTableObj’s selectedRowIndexes()
  
  
set chooseItems to ((aTableObj’s dataSource())’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list
  
set _retrieve_data to {}
  
repeat with anItem in chooseItems
    set _retrieve_data’s end to anItem’s fileURL
  end repeat
end raizeAlert:

## ContentView を作成
on makeContentView(paramObj)
  ## 準備
  
set fileURLs to (paramObj’s fileURLs) as list
  
set thisFileType to (paramObj’s fileType) as text
  
  
set keyrec to {column1:"Name"}
  
set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyrec)
  
  
set theDataSource to current application’s YKZArrayController’s new()
  
  
## NSTableView
  
tell current application’s NSTableView’s alloc()
    tell initWithFrame_(current application’s CGRectZero)
      setAllowsEmptySelection_(false)
      
setAllowsMultipleSelection_(true)
      
setDataSource_(theDataSource)
      
setDelegate_(theDataSource)
      
setDoubleAction_("doubleAction:")
      
setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask)
      
setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular)
      
setTarget_(theDataSource)
      
setUsesAlternatingRowBackgroundColors_(true)
      
      
set thisRowHeight to rowHeight() as integer
      
set theDataSource’s _table_view to it
    end tell
  end tell
  
  
# data sourceに追加
  
tell (theDataSource)
    awakeFromNib()
    
set its _file_type to thisFileType
    
    
repeat with aURL in fileURLs
      addObject_({fileURL:aURL})
    end repeat
    
    
setSelectionIndex_(0)
  end tell
  
  
## NSTableColumn
  
### Columnの並び順を指定する
  
set viewWidth to 320
  
set columnsCount to keyDict’s |count|()
  
repeat with colNum from 1 to columnsCount
    
    
set keyName to "column" & colNum as text
    
set aTitle to (keyDict’s objectForKey:keyName)
    
    
tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text))
      tell headerCell()
        setStringValue_(aTitle)
        
set thisHeaderHeight to cellSize()’s height
      end tell
      
      
### Columnの横幅を指定
      
setWidth_(viewWidth)
      
      
### バインディングのオプション
      
— ソートを無効にする
      
set anObj to (current application’s NSNumber’s numberWithBool:false)
      
set aKey to current application’s NSCreatesSortDescriptorBindingOption
      
set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey)
      
      
### 表示内容をNSArrayControllerにバインディング
      
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, theDataSource, "arrangedObjects", bOptions)
      
      (
theDataSource’s _table_view’s addTableColumn:it)
    end tell
    
  end repeat
  
  
## NSScrollView
  
### Viewの高さを計算
  
set rowCount to 12 –> 行数を固定
  
set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない
  
set vSize to current application’s NSMakeRect(0, 0, viewWidth, viewHeight)
  
  
### Viewを作成
  
tell current application’s NSScrollView’s alloc()
    tell initWithFrame_(vSize)
      setBorderType_(current application’s NSBezelBorder)
      
setDocumentView_(theDataSource’s _table_view)
      
–setHasHorizontalScroller_(true)
      
setHasVerticalScroller_(true)
      
set aScroll to it
    end tell
  end tell
  
  
return {aScroll, theDataSource’s _table_view}
end makeContentView

★Click Here to Open This Script 

AppleScript名:subClasses
#MARK: – NSValueTransformer
script YKZURLToIcon
  property parent : class "NSValueTransformer"
  
property allowsReverseTransformation : false –>逆変換
  
property transformedValueClass : a reference to current application’s NSImage –>クラス
  
  
#変換処理
  
on transformedValue:fileURL
    if fileURL is missing value then return
    
    
set appPath to fileURL’s |path|()
    
set iconImage to current application’s NSWorkspace’s sharedWorkspace’s iconForFile:appPath
    
return iconImage
  end transformedValue:
end script

script YKZURLToDisplayedName
  property parent : class "NSValueTransformer"
  
property allowsReverseTransformation : false –>逆変換
  
property transformedValueClass : a reference to current application’s NSString –>クラス
  
  
#変換処理
  
on transformedValue:fileURL
    if fileURL is missing value then return
    
    
set appPath to fileURL’s |path|()
    
set displayedName to current application’s NSFileManager’s defaultManager’s displayNameAtPath:appPath
    
return displayedName
  end transformedValue:
end script

#MARK: – Array Controller
script YKZArrayController
  property parent : class "NSArrayController"
  
  
#MARK: IBOutlets
  
property _table_view : missing value
  
#MARK:
  
property _data_type : do shell script "uuidgen"
  
property _file_type : ""
  
  
on awakeFromNib()
    –log "YKZArrayController – awakeFromNib"
    
tell _table_view
      registerForDraggedTypes_({current application’s NSFilenamesPboardType, _data_type})
      
setDraggingSourceOperationMask_forLocal_(current application’s NSDragOperationCopy, false)
    end tell
    
    
#Transformerの登録
    
set tNames to {}
    
set tNames’s end to "YKZURLToIcon"
    
set tNames’s end to "YKZURLToDisplayedName"
    
repeat with aTransformer in tNames
      set theTransformer to current application’s class aTransformer’s new()
      (
current application’s NSValueTransformer’s setValueTransformer:theTransformer forName:aTransformer)
    end repeat
  end awakeFromNib
  
  
#MARK: Data Source Overrides
  
on numberOfRowsInTableView:aTableView
    return my content()’s |count|()
  end numberOfRowsInTableView:
  
  
on tableView:aTableView viewForTableColumn:aColumn row:aRow
    –log "viewForTableColumn"
    
    
set aCellView to aTableView’s makeViewWithIdentifier:"YKZTableCellView" owner:me
    
–set isNull to aCellView’s isEqual:(current application’s NSNull’s |null|())
    
–log isNull as text
    
    
if aCellView is missing value then
      
      
set frameRect to current application’s NSMakeRect(0, 0, aColumn’s width, aTableView’s rowHeight())
      
set aCellView to current application’s YKZTableCellView’s alloc’s initWithFrame:frameRect
      
      
set anObj to "YKZURLToIcon"
      
set aKey to current application’s NSValueTransformerNameBindingOption
      
set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey)
      (
aCellView’s imageView())’s bind:(current application’s NSValueBinding) toObject:aCellView withKeyPath:"objectValue.fileURL" options:bOptions
      
      
set anObj to "YKZURLToDisplayedName"
      
set aKey to current application’s NSValueTransformerNameBindingOption
      
set bOptions to (current application’s NSDictionary’s dictionaryWithObject:anObj forKey:aKey)
      (
aCellView’s textField())’s bind:(current application’s NSValueBinding) toObject:aCellView withKeyPath:"objectValue.fileURL" options:bOptions
      
    end if
    
    
return aCellView
  end tableView:viewForTableColumn:row:
  
  
# テーブル内のセルが編集できるか
  
on tableView:aTableView shouldEditTableColumn:aColumn row:aRow
    return false
  end tableView:shouldEditTableColumn:row:
  
  
# テーブル内をダブルクリックしたらOKボタンを押す
  
on doubleAction:sender
    log "doubleAction"
    
## ヘッダをクリックした時は何もしない
    
if (sender’s clickedRow()) is -1 then return
    
    
set theEvent to current application’s NSEvent’s ¬
      keyEventWithType:(current application’s NSEventTypeKeyDown) ¬
        location:(current application’s NSZeroPoint) ¬
        
modifierFlags:0 ¬
        
timestamp:0.0 ¬
        
windowNumber:(sender’s |window|()’s windowNumber()) ¬
        
context:(current application’s NSGraphicsContext’s currentContext()) ¬
        
|characters|:return ¬
        
charactersIgnoringModifiers:(missing value) ¬
        
isARepeat:false ¬
        
keyCode:0
    current application’s NSApp’s postEvent:theEvent atStart:(not false)
  end doubleAction:
  
  
#MARK: Drag Operation Method
  
#ドラッグを開始(ペーストボードに書き込む)
  
on tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard
    –log "writeRowsWithIndexes"
    
set aData to current application’s NSKeyedArchiver’s archivedDataWithRootObject:rowIndexes
    
pboard’s declareTypes:{_data_type} owner:(missing value)
    
pboard’s setData:aData forType:_data_type
    
return true
  end tableView:writeRowsWithIndexes:toPasteboard:
  
#ドラッグ途中
  
on tableView:aTableView validateDrop:info proposedRow:row proposedDropOperation:operation
    –log "validateDrop"
    
#列の間にドラッグ
    
if (operation is current application’s NSTableViewDropAbove) then
      return current application’s NSDragOperationMove
    end if
    
#項目の上にドラッグ
    
set aTypes to info’s draggingPasteboard’s types()
    
if (aTypes’s containsObject:_data_type) as boolean then
      return current application’s NSDragOperationNone
    end if
    
return current application’s NSDragOperationNone
  end tableView:validateDrop:proposedRow:proposedDropOperation:
  
#ドラッグ終了
  
on tableView:aTableView acceptDrop:info row:row dropOperation:operation
    –log "acceptDrop"
    
#
    
set pboard to info’s draggingPasteboard()
    
set aTypes to pboard’s types()
    
    
if (aTypes’s containsObject:_data_type) as boolean then
      
      
set rowData to pboard’s dataForType:_data_type
      
set rowIndexes to current application’s NSKeyedUnarchiver’s unarchiveObjectWithData:rowData
      
      
if (rowIndexes’s firstIndex()) < row then
        set row to row – (rowIndexes’s |count|())
      end if
      
      
set aRange to current application’s NSMakeRange(row, rowIndexes’s |count|())
      
set aIndexSet to current application’s NSIndexSet’s indexSetWithIndexesInRange:aRange
      
      
set anObj to ((my content())’s objectsAtIndexes:rowIndexes)
      
my removeObjects:anObj
      
my insertObjects:anObj atArrangedObjectIndexes:aIndexSet
      
my rearrangeObjects()
      
    else
      set pathList to pboard’s propertyListForType:(current application’s NSFilenamesPboardType)
      
–log result as list
      
repeat with aPath in pathList
        set isAdd to addItem_({filePath:aPath, atIndex:row})
        
if isAdd then beep
      end repeat
      
    end if
    
return true
  end tableView:acceptDrop:row:dropOperation:
  
  
#追加する
  
on addItem:sender –> {filePath:aPath, atIndex:aIndex}
    –log "addItem:"
    
set isAdd to false
    
    
#missing valueの時は最後に追加
    
set {filePath:aPath, atIndex:aIndex} to sender
    
if aIndex is missing value then
      set aIndex to my content()’s |count|() as number
    end if
    
    
#URLに変換
    
set appURL to (current application’s NSURL’s fileURLWithPath:aPath)
    
#アプリケーション以外は追加しない
    
set {aResult, aUTI, aError} to (appURL’s getResourceValue:(reference) ¬
      forKey:(current application’s NSURLTypeIdentifierKey) ¬
      
|error|:(reference))
    log aUTI
    
if (aUTI as text) is not (my _file_type as text) then return isAdd
    
    
#すでにあるか確認
    
set bList to my content() as list
    
repeat with aRecord in bList
      if ((appURL’s isEqual:(aRecord’s fileURL)) as boolean) then
        set isAdd to true
        
exit repeat
      end if
    end repeat
    
    
#なければ追加
    
if not isAdd then
      set anObj to {fileURL:appURL}
      (
my insertObject:anObj atArrangedObjectIndex:aIndex)
    end if
    
return isAdd
  end addItem:
end script

#MARK: – NSTableCellView
script YKZTableCellView
  property parent : class "NSTableCellView"
  
  
on initWithFrame:rect
    –log "initWithFrame"
    
continue initWithFrame:rect
    
    
setAutoresizingMask_(current application’s NSViewWidthSizable)
    
    
tell current application’s NSImageView’s alloc
      tell initWithFrame_(current application’s NSMakeRect(0, 0, 16, 16))
        setImageScaling_(current application’s NSImageScaleProportionallyUpOrDown)
        
setImageAlignment_(current application’s NSImageAlignCenter)
        
        
my setImageView:it
        
my addSubview:it
      end tell
    end tell
    
    
tell current application’s NSTextField’s alloc
      tell initWithFrame_(current application’s NSMakeRect(20, 2, 200, 14))
        setBordered_(false)
        
setDrawsBackground_(false)
        
setEditable_(false)
        
        
my setTextField:it
        
my addSubview:it
      end tell
    end tell
    
    
return me
  end initWithFrame:
end script

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSArrayController NSBundle NSCreatesSortDescriptorBindingOption NSDictionary NSNumber NSScrollView NSTableCellView NSTableView NSURL NSValueBinding NSValueTransformer | Leave a comment

RoundWindow v2

Posted on 1月 12, 2020 by Takaaki Naganoya

Edama2さんと「無理だよねー」「そうそう、絶対無理〜」などとメールで言っていたら、Shane Stanleyから届いた「できるよ」という衝撃のメール。

スクリプトエディタ上で記述する通常のAppleScriptで、CocoaのCustom Class宣言と呼び出しができるとのこと。

自分で動作確認してみるまで、半信半疑でしたが、、、、できますねこれは、、、

–> Download Editable and Executable Script Bundle

冗談半分で思いついたことを試してみたらできてしまったり、冗談半分でできるわけないよねと念のために書いておいたことが世界の誰かの目に止まったりと、「冗談半分」ってけっこう重要なことだと思えてきました。

以下、Shane Stanleyの説明による、その書き換え手順です。

(1)Subclassファイル(複数可)をXcode上のプロジェクトで書くようなスタイルで書く

こういう(↑)スタイルですね。かならずscript宣言しつつ、parent属性を宣言しておくところがXcode上のAppleScriptアプリケーションのスタイルです。あとで動作確認して、アプリケーションの起動や終了に関するイベントハンドラを書いておいたのは無駄(実行されない)ではないかとも思われました。

(2)実行するメインのScriptのResourcesフォルダ内にSubclassファイルを入れる

普通、AppleScriptのファイルが入る/Contents/Resources/Scripts/でも、ライブラリを入れておく/Contents/Resources/Script Libraries/でもなく、/Contents/Resources/の直下に入れます。ファイル名はオリジナルのEdama2さんのものをそのまま採用していますが、割となんでもいいようです。Custom Classファイルは分割してもいいし、このサンプルのようにまとめてもいいんでしょう。

# 追加実験してみたところ、Resourcesフォルダ以下の/Scriptsや/Script Libraries/フォルダと重複しない名称の別フォルダ(例:/Classes/)に入れておいても大丈夫でした

(3)use framework “AppleScriptObjC”の宣言文を追加

見たことのない光景ですが、書くことについてはとくに障害はありません。AppKit.Frameworkもuse宣言しておいたほうがよかったかもしれません。

(4)メインスクリプトの実行時に以下の処理を実行

set theBundle to current application’s NSBundle’s bundleWithPath:pathToFolderWithScripts
theBundle’s loadAppleScriptObjectiveCScripts()

★Click Here to Open This Script 

試行錯誤して、上記の「pathToFolderWithScripts」にバンドル内の/Contents/Resources/を入れて実行すればよいことが理解できました。

以上の変更を加えて、ためしにスクリプトエディタ&Script Debugger上で実行してみたところ、改変前と変わりなく実行できてしまいました(冒頭のスクリーンショット)。

いや、これはめちゃくちゃすごいですよ!! 何がすごいって、CocoaのCustom Classをスクリプトライブラリ中に入れて呼び出せるということで、けっこう無茶な箱庭インタフェースが作れてしまう予感が、、、、。

そして、AppleScriptObjC(AppleScriptObjC.frameworkより)でスクリプトエディタの「.scpt」形式のファイルを読み込んで実行できてしまったということは、Xcode上のAppleScriptアプリケーション内のScriptもテキスト形式だけでなく、スクリプトエディタで編集できる.scpt形式のファイルを突っ込んで編集できる可能性が見えてきました。

ただ、テキスト形式になっていないと、Interface Builderとの連携のあたりで問題になりそうな気もします。

AppleScript名:customClassTest.scptd
—
–  Created by: Edama2 2020/01/10
–  Adviced by: Shane Stanley 2020/01/11
–  Modified by: Takaaki Naganoya 2020/01/12
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppleScriptObjC"
use scripting additions

property _clock_text_view : missing value –> 時計用の文字列
property _clock_timer : missing value –> 時計用のNSTimer

–Script Bundle内のResourcesフォルダを求める
set resourcePath to POSIX path of (path to me) & "Contents/Resources/"
set theBundle to current application’s NSBundle’s bundleWithPath:resourcePath
theBundle’s loadAppleScriptObjectiveCScripts()

my performSelectorOnMainThread:"raizeWindow:" withObject:(missing value) waitUntilDone:true

# ウィンドウを作成
on raizeWindow:aParam
  
  
# 時計用の文字を作成
  
tell current application’s NSTextView’s alloc()
    tell initWithFrame_(current application’s NSMakeRect(35, 120, 300, 40))
      setRichText_(true)
      
useAllLigatures_(true)
      
setTextColor_(current application’s NSColor’s whiteColor())
      
setFont_(current application’s NSFont’s fontWithName:"Arial-Black" |size|:48)
      
setBackgroundColor_(current application’s NSColor’s colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.0)
      
setAlphaValue_(1.0)
      
setEditable_(false)
      
–setString_("00:00:00")
      
      
set my _clock_text_view to it
    end tell
  end tell
  
  
# 時計を更新するNSTimerを作成
  
set my _clock_timer to current application’s NSTimer’s scheduledTimerWithTimeInterval:1 target:me selector:"idleHandler:" userInfo:(missing value) repeats:true
  
  
# 丸いViewを作成
  
set aFrame to current application’s NSMakeRect(0, 0, 300, 300)
  
tell current application’s RoundView’s alloc()
    tell initWithFrame_(aFrame)
      setNeedsDisplay_(true)
      
setSubviews_({my _clock_text_view})
      
set customView to it
    end tell
  end tell
  
  
#スクリーンのサイズを調べる
  
set aScreen to current application’s NSScreen’s mainScreen()
  
  
# Window
  
set aBacking to current application’s NSWindowStyleMaskBorderless
  
–set aBacking to current application’s NSBorderlessWindowMask
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
tell current application’s CustomWindow’s alloc()
    tell initWithContentRect_styleMask_backing_defer_screen_(aFrame, aBacking, aDefer, false, aScreen)
      –setTitle_(uniqueName) –>タイトル
      
setBackgroundColor_(current application’s NSColor’s clearColor()) — Grammar Police –>背景色
      
setContentView_(customView) –>内容ビューのセット
      
setDelegate_(me) –>デリゲート
      
setDisplaysWhenScreenProfileChanges_(true) –>スクリーンプロファイルが変更されたときウインドウの内容をアップデートするか
      
setHasShadow_(true) –>ウインドウに影があるか
      
setIgnoresMouseEvents_(false) –>マウスイベントを無視するか
      
–setLevel_((current application’s NSScreenSaverWindowLevel) + 1) –>ウインドウの前後関係の位置
      
setOpaque_(false) –>ウインドウを不透明にするか
      
setReleasedWhenClosed_(true) –>閉じたときにメモリを解放するか
      
      
#
      
|center|()
      
makeKeyAndOrderFront_(me) –>キーウインドウにして前面に持ってくる
      
–setFrame_display_(aFrame, true) –>表示
    end tell
  end tell
end raizeWindow:

#タイマー割り込み
on idleHandler:aSender
  set mesStr to time string of (current date)
  (
my _clock_text_view)’s setString:mesStr
end idleHandler:

★Click Here to Open This Script 

AppleScript名:CocoaAppletAppDelegate.scpt
script CocoaAppletAppDelegate
  property parent : class "NSObject"
  
  
on applicationWillFinishLaunching:aNotification
    —
  end applicationWillFinishLaunching:
  
  
  
on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
end script

script CustomWindow
  property parent : class "NSWindow"
  
property canBecomeKeyWindow : true
  
  
property _initial_location : missing value
  
  
on mouseDown:theEvent
    set my _initial_location to theEvent’s locationInWindow()
  end mouseDown:
  
  
on mouseDragged:theEvent
    –set res to display dialog "mouseDragged" buttons {"OK"} default button "OK"
    
try
      set screenVisibleFrame to current application’s NSScreen’s mainScreen()’s visibleFrame()
      
set screenVisibleFrame to my myHandler(screenVisibleFrame)
      
set windowFrame to my frame()
      
set windowFrame to my myHandler(windowFrame)
      
set newOrigin to windowFrame’s origin
      
      
set currentLocation to theEvent’s locationInWindow()
      
set newOrigin’s x to (newOrigin’s x) + ((currentLocation’s x) – (_initial_location’s x))
      
set newOrigin’s y to (newOrigin’s y) + ((currentLocation’s y) – (_initial_location’s y))
      
      
set tmpY to ((newOrigin’s y) + (windowFrame’s |size|’s height))
      
set screenY to (screenVisibleFrame’s origin’s y) + (screenVisibleFrame’s |size|’s height)
      
if tmpY > screenY then
        set newOrigin’s y to (screenVisibleFrame’s origin’s y) + ((screenVisibleFrame’s |size|’s height) – (windowFrame’s |size|’s height))
      end if
      
      
my setFrameOrigin:newOrigin
    on error error_message number error_number
      set error_text to "Error: " & error_number & ". " & error_message
      
display dialog error_text buttons {"OK"} default button 1
      
return error_text
    end try
    
  end mouseDragged:
  
  
on myHandler(aFrame)
    if class of aFrame is list then
      set {{theX, theY}, {theWidth, theHeight}} to aFrame
      
set aFrame to {origin:{x:theX, y:theY}, |size|:{width:theWidth, height:theHeight}}
      
–set aFrame to current application’s NSMakeRect(theX, theY, theWidth, theHeight)
    end if
    
return aFrame
  end myHandler
end script

script RoundView
  property parent : class "NSView"
  
  
on drawRect:rect
    set aFrame to my frame()
    
set myColor to current application’s NSColor’s redColor()
    
    
tell current application’s NSBezierPath
      tell bezierPathWithOvalInRect_(aFrame)
        myColor’s |set|()
        
fill()
      end tell
    end tell
  end drawRect:
end script

★Click Here to Open This Script 

Posted in Custom Class GUI How To | Tagged 10.14savvy 10.15savvy NSBezierPath NSBundle NSColor NSFont NSScreen NSTextView NSTimer NSView NSWindow | Leave a comment

RoundWindow

Posted on 1月 10, 2020 by Takaaki Naganoya

本Scriptは、Edama2さんから投稿していただいたものです。Cocoa AppleScript AppletでCustom Classを宣言して作られた、丸いウィンドウ(透明ウィンドウの上に丸いグラフィックを描画)を表示して、タイマー割り込みで時計を表示するAppleScriptです。

–> Download Editable and Executable Applet

AppleScriptのランタイム環境はいくつかあり、それぞれに「できること」と「できないこと」、「手軽さ」などが異なります。

(1)スクリプトエディタ上で記述、実行する環境
一番セキュリティ上の制約が緩く、できることの多い環境です。

(2)Script Debugger上で記述、実行する環境
Cocoaのイベントやオブジェクトのログ表示などができる環境です。

(3)Applet環境
AppleScriptの実行ファイルです。

(4)Script Menu環境
macOS標準装備の、メニューからAppleScriptを実行できる環境です。

さらに、Cocoa Scriptingの機能に着目してみると、見え方が変わります。

本Scriptを記述している「Cocoa AppleScript Applet」環境(上の図の赤い部分)は、スクリプトエディタ上で記述する通常のAppleScriptと、Xcode上で記述するCocoaアプリケーションの中間的な性格を持つものです。スクリプトエディタ上で直接は動かせず、アプレット形式で動作させることになりますが、スクリプトエディタ上で動かすよりも、よりCocoaの機能が利用できます。

Cocoa AppleScript Appletでは、アプリケーション(Applet)起動や終了の最中で発生するイベントを利用できますし、本ScriptのようにCocoaのCustom Classを宣言できます。これは、普通のスクリプトエディタ上で記述する(本Blogの大部分のScriptのような)Scriptではできない真似です。
→ Shane Stanleyからツッコミが入って、手の込んだ作業を行うとできるとかで(テンプレートそのままでは無理)、後で実際に試してみます

タイトルは「丸いウィンドウと時計の表示」
NSWindowのカスタムクラスを使ったタイトルバーなしのドラッグで移動できる丸いウィンドウとオマケに時計を表示したものです。
初心者受けしそうなやつです。
問題はそこではなく、
XcodeやCocoa applescript appletから実行するASOCだとカスタムクラスが作れるけど、
ノーマルのapplescriptから実行するASOCではカスタムクラスが作れないということです。
表現がややこしいですが...。
ノーマルのapplescriptからカスタムクラスを作ると、ただのスクリプトオブジェクトにしかなりません。
誰かうまい解決方法を知っている人がいたら教えてください。

ちょうど、こういう資料をまとめていたので補足説明に役立ってしまいました。スクリプトエディタ上で記述する通常のAppleScriptでもCustom Cocoa Classが宣言できると便利そうですが、どんなもんでしょうか? 

Custom Classは便利なので使いたくなる一方、AppleScriptのインタプリタ上で実行するため、Objective-Cなどで書くのと同じような感覚で使うと「遅くて使えない」という話になると思いますが、このEdama2さんのサンプルぐらいの使いかたであれば、ちょうどいいんじゃないかというところです。

歴史的にみると、Cocoa-AppleScript Appletは、Xcode上で記述するCocoa-Applicationを簡略化してスクリプトエディタ上でCocoa Scriptingを手軽に使えるように手直しした「途中」の環境といえます。

Cocoa-AppleScript Appletは、GUIが手軽に作れるわけでもなく、スクリプトエディタ上で直接実行やログ表示ができるわけでもなありません。マスターしたところで最終到達点がCocoaアプリケーションほど高くなく、編集や習熟もしづらいことから「中途半端」「使えない」という評価になっていました(自分も使っていませんでした)。

その後、Cocoa-AppleScript Appletの機能要素をさらにダウンサイジングして、スクリプトエディタ上で手軽に記述・実行ができるように進化したのが現在・広くつかわれているCocoa Scripting環境です。

ただ、使いやすくなって広く使われるようになったはいいものの、「Xcodeを使うまでもないが、もうちょっとCocoaの機能が利用できないか?」という意見も出るようになり、Cocoa-AppleScript Appletを再評価してもいいんじゃないかと考えるようになってきてはいます。

ちなみに、本Cocoa-AppleScript AppletでCustom Classを宣言しているのと同じような書き方で、通常のCocoa Scriptingの環境で動かすような変更を加えたScriptもEdama2さんが試していますが、それは「動かない」ということで結論が出ています。

AppleScript名:CocoaAppletAppDelegate.scpt
script CocoaAppletAppDelegate
  property parent : class "NSObject"
  
  
property _clock_text_view : missing value –> 時計用の文字列
  
property _clock_timer : missing value –> 時計用のNSTimer
  
  
on applicationWillFinishLaunching:aNotification
    my raizeWindow()
  end applicationWillFinishLaunching:
  
  
on applicationShouldTerminate:sender
    my _clock_timer’s invalidate()
    
return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
# ウィンドウを作成
  
on raizeWindow()
    
    
# 時計用の文字を作成
    
tell current application’s NSTextView’s alloc()
      tell initWithFrame_(current application’s NSMakeRect(35, 120, 300, 40))
        setRichText_(true)
        
useAllLigatures_(true)
        
setTextColor_(current application’s NSColor’s whiteColor())
        
setFont_(current application’s NSFont’s fontWithName:"Arial-Black" |size|:48)
        
setBackgroundColor_(current application’s NSColor’s colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.0)
        
setAlphaValue_(1.0)
        
setEditable_(false)
        
–setString_("00:00:00")
        
        
set my _clock_text_view to it
      end tell
    end tell
    
    
# 時計を更新するNSTimerを作成
    
set my _clock_timer to current application’s NSTimer’s scheduledTimerWithTimeInterval:1 target:me selector:"idleHandler:" userInfo:(missing value) repeats:true
    
    
    
# 丸いViewを作成
    
set aFrame to current application’s NSMakeRect(0, 0, 300, 300)
    
tell current application’s RoundView’s alloc()
      tell initWithFrame_(aFrame)
        setNeedsDisplay_(true)
        
setSubviews_({my _clock_text_view})
        
set customView to it
      end tell
    end tell
    
    
#スクリーンのサイズを調べる
    
set aScreen to current application’s NSScreen’s mainScreen()
    
    
# Window
    
set aBacking to current application’s NSWindowStyleMaskBorderless
    
–set aBacking to current application’s NSBorderlessWindowMask
    
set aDefer to current application’s NSBackingStoreBuffered
    
tell current application’s CustomWindow’s alloc()
      tell initWithContentRect_styleMask_backing_defer_screen_(aFrame, aBacking, aDefer, false, aScreen)
        –setTitle_(uniqueName) –>タイトル
        
setBackgroundColor_(current application’s NSColor’s clearColor()) –>背景色
        
setContentView_(customView) –>内容ビューのセット
        
setDelegate_(me) –>デリゲート
        
setDisplaysWhenScreenProfileChanges_(true) –>スクリーンプロファイルが変更されたときウインドウの内容をアップデートするか
        
setHasShadow_(true) –>ウインドウに影があるか
        
setIgnoresMouseEvents_(false) –>マウスイベントを無視するか
        
–setLevel_((current application’s NSScreenSaverWindowLevel) + 1) –>ウインドウの前後関係の位置
        
setOpaque_(false) –>ウインドウを不透明にするか
        
setReleasedWhenClosed_(true) –>閉じたときにメモリを解放するか
        
        
#
        
|center|()
        
makeKeyAndOrderFront_(me) –>キーウインドウにして前面に持ってくる
        
–setFrame_display_(aFrame, true) –>表示
      end tell
    end tell
  end raizeWindow
  
  
#タイマー割り込み
  
on idleHandler:aSender
    set mesStr to time string of (current date)
    (
my _clock_text_view)’s setString:mesStr
  end idleHandler:
end script

script CustomWindow
  property parent : class "NSWindow"
  
property canBecomeKeyWindow : true
  
  
property _initial_location : missing value
  
  
on mouseDown:theEvent
    set my _initial_location to theEvent’s locationInWindow()
  end mouseDown:
  
  
on mouseDragged:theEvent
    –set res to display dialog "mouseDragged" buttons {"OK"} default button "OK"
    
try
      set screenVisibleFrame to current application’s NSScreen’s mainScreen()’s visibleFrame()
      
set screenVisibleFrame to my myHandler(screenVisibleFrame)
      
set windowFrame to my frame()
      
set windowFrame to my myHandler(windowFrame)
      
set newOrigin to windowFrame’s origin
      
      
set currentLocation to theEvent’s locationInWindow()
      
set newOrigin’s x to (newOrigin’s x) + ((currentLocation’s x) – (_initial_location’s x))
      
set newOrigin’s y to (newOrigin’s y) + ((currentLocation’s y) – (_initial_location’s y))
      
      
set tmpY to ((newOrigin’s y) + (windowFrame’s |size|’s height))
      
set screenY to (screenVisibleFrame’s origin’s y) + (screenVisibleFrame’s |size|’s height)
      
if tmpY > screenY then
        set newOrigin’s y to (screenVisibleFrame’s origin’s y) + ((screenVisibleFrame’s |size|’s height) – (windowFrame’s |size|’s height))
      end if
      
      
my setFrameOrigin:newOrigin
    on error error_message number error_number
      set error_text to "Error: " & error_number & ". " & error_message
      
display dialog error_text buttons {"OK"} default button 1
      
return error_text
    end try
    
  end mouseDragged:
  
  
on myHandler(aFrame)
    if class of aFrame is list then
      set {{theX, theY}, {theWidth, theHeight}} to aFrame
      
set aFrame to {origin:{x:theX, y:theY}, |size|:{width:theWidth, height:theHeight}}
      
–set aFrame to current application’s NSMakeRect(theX, theY, theWidth, theHeight)
    end if
    
return aFrame
  end myHandler
end script

script RoundView
  property parent : class "NSView"
  
  
on drawRect:rect
    set aFrame to my frame()
    
set myColor to current application’s NSColor’s redColor()
    
    
tell current application’s NSBezierPath
      tell bezierPathWithOvalInRect_(aFrame)
        myColor’s |set|()
        
fill()
      end tell
    end tell
  end drawRect:
end script

★Click Here to Open This Script 

Posted in GUI | Tagged 10.14savvy 10.15savvy NSBezierPath NSColor NSScreen NSTextView NSTimer NSView NSWindow | Leave a comment

display text fields Library v1.3

Posted on 12月 31, 2019 by Takaaki Naganoya

「display text fields」AppleScriptライブラリをv1.3にアップデートしました。

–> Download display text fields_v13 (To ~/Library/Script Libraries/)

v1.3ではテキストフィールドの最大横幅の計算を修正し、フィールド数が増えたときにダイアログ上から項目がはみ出さないようにScroll Viewをつけるようにしました。

本来目的としていた用途に使ってみたらイマイチだった点を修正した格好です。AppleScriptで取得した各種アプリケーションのオブジェクトのプロパティ情報をダイアログ上で一覧表示して確認するというのが、自分がこのライブラリを作った目的です。

以下は、サンプルスクリプト「文字列で指定したAppleScriptの実行結果をテキストで取得する v2」についての説明です。

割とえげつない処理をしていますが、作りためておいたルーチンを引っ張り出してきただけなので、書くのにさほど時間はかけていません。

こうしたAppleScriptのプロパティ値をparseするには、スクリプトエディタの結果欄を文字列として取得するか(GUI Scripting経由でやったことがあります)、こうしてメモリ上にスクリプトエディタ+結果表示用のビューを生成してメモリ上でAppleScriptを実行して結果をテキストで取得するということになると思います。前者だとGUI Scriptingの実行権限が必要になるため、Cocoaの機能を利用したほうが手軽という状況です。

(途中で入れ替えた)v2では、macOS 10.15対応、ランタイム環境によってはうまく動かない「as anything」の使用をやめるなどの変更を加えました。

AppleScript名:文字列で指定したAppleScriptの実行結果をテキストで取得する v2.scpt
— Created 2016-01-08 by Takaaki Naganoya
— 2016 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "OSAKit"
use framework "AppKit"
use tfLib : script "display text fields"

property OSAScript : a reference to current application’s OSAScript
property NSTextView : a reference to current application’s NSTextView
property OSAScriptView : a reference to current application’s OSAScriptView
property OSAScriptController : a reference to current application’s OSAScriptController

property myRes : ""

–OSのメジャーバージョンを数値で取得
set osVer to system attribute "sys2"

if osVer ≥ 15 then
  set srcStr to "tell application \"Music\"
  set aSel to first item of selection
  set aRes to (properties of aSel)
end tell"
else
  set srcStr to "tell application \"iTunes\"
  set aSel to first item of selection
  set aRes to (properties of aSel)
end tell"
end if

my performSelectorOnMainThread:"getResultStringFromScript:" withObject:(srcStr) waitUntilDone:true

set aRes to getListFromText(myRes) of me

set aList to {}
set bList to {}
set aLen to length of aRes

repeat with i from 1 to aLen
  set {aCon, bCon} to contents of item i of aRes
  
set the end of aList to aCon
  
set the end of bList to bCon
end repeat

confirm text fields main message "Track Info" sub message "Properties about selected track" key list aList value list bList

–Get AppleScript’s Result as string
on getResultStringFromScript:paramObj
  set srcStr to paramObj as string
  
set myRes to ""
  
  
set targX to 500 –View Width
  
set targY to 200 –View Height
  
  
set osaCon to OSAScriptController’s alloc()’s init()
  
set osaView to OSAScriptView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
  
set resView to NSTextView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, targX, targY))
  
resView’s setRichText:true
  
resView’s useAllLigatures:true
  
  
osaCon’s setScriptView:osaView
  
osaCon’s setResultView:resView
  
  
osaView’s setString:srcStr
  
osaCon’s runScript:(missing value)
  
  
set myRes to resView’s |string|() as string
end getResultStringFromScript:

–スクリプトエディタのresult欄に返ってきたテキストをリストに変える
on getListFromText(aText)
  
  
script getListFromTextO
    property aaText : ""
    
property gList : {}
    
property outList : {}
    
property aG : ""
    
property valList : {}
  end script
  
  
copy aText to (aaText of getListFromTextO)
  
  
set (gList of getListFromTextO) to {}
  
set (outList of getListFromTextO) to {}
  
set (aG of getListFromTextO) to ""
  
set (valList of getListFromTextO) to {}
  
  
if (aaText of getListFromTextO) does not start with "{" and (aaText of getListFromTextO) does not end with "}" then
    return {}
  end if
  
  
set aLen to length of (aaText of getListFromTextO)
  
set (aG of getListFromTextO) to text 2 thru -2 of (aaText of getListFromTextO)
  
set (gList of getListFromTextO) to characters of (aG of getListFromTextO)
  
  
  
set sPos to 2 –1文字目は\"{\"なので2文字目からスキャンを開始する
  
set ePos to 2
  
  
set imdF to false –Immediate Data Flag(文字列中を示すダブルクォート内の場合にはtrueになる)
  
set listF to 0 –stacking段数が入る
  
  
set attrF to true –属性ラベルスキャン時にtrue、データ末尾スキャン時にfalse
  
  
  
repeat with i in (gList of getListFromTextO)
    
    
set j to contents of i
    
    
if attrF = true and imdF = false and listF = 0 then
      
      
–属性値部分の末尾検出
      
if j = ":" then
        if text sPos thru sPos of (aaText of getListFromTextO) = " " then
          set sPos to sPos + 1
        end if
        
set anOut to text sPos thru ePos of (aaText of getListFromTextO)
        
set sPos to ePos + 1
        
set the end of (valList of getListFromTextO) to anOut
        
set attrF to false –データのスキャンを開始する
        
set imdF to false
        
set listF to 0
      end if
      
    else if imdF = false and listF = 0 and j = "," then
      
      
–データ部分の末尾検出
      
set anOut to text sPos thru (ePos – 1) of (aaText of getListFromTextO)
      
set sPos to ePos + 1
      
set the end of (valList of getListFromTextO) to anOut
      
set the end of (outList of getListFromTextO) to (valList of getListFromTextO)
      
set (valList of getListFromTextO) to {}
      
      
set attrF to true –次が属性値ラベルであることを宣言
      
set imdF to false
      
set listF to 0
      
    else if j = "{" then
      if imdF = false then
        set listF to listF + 1 –1段スタックにpush
      end if
    else if j = "}" then
      if imdF = false then
        set listF to listF – 1 –1段スタックからpop
      end if
    else if j = "\"" then
      if imdF = true then
        set imdF to false
      else
        set imdF to true
      end if
    end if
    
    
set ePos to ePos + 1
    
  end repeat
  
  
–ラストのデータ部分を出力
  
try
    set the end of (valList of getListFromTextO) to text sPos thru (ePos – 1) of (aaText of getListFromTextO)
    
set the end of (outList of getListFromTextO) to (valList of getListFromTextO)
  on error
    false
  end try
  
  
return contents of (outList of getListFromTextO)
  
end getListFromText

★Click Here to Open This Script 

Posted in dialog GUI OSA | Tagged 10.13savvy 10.14savvy 10.15savvy iTunes NSTextView OSAScript OSAScriptController OSAScriptView | Leave a comment

アラートダイアログをタイトル付き表示

Posted on 12月 27, 2019 by Takaaki Naganoya

アラートダイアログで、ウィンドウタイトルを表示させるAppleScriptです。

AppleScriptには登場当初からダイアログの表示命令「display dialog」が備わっていました。久しぶりにClassic MacOSのdisplay dialogコマンドの表示を見ると、あまりの素朴さに驚かされます。

display dialogは手軽であるもののモーダルなダイアログなので、その後のMac OS Xへの移行時にはいまひとつ合わない感じがしていました。完全にMac OS Xの流儀で書き直されたのが、「display alert」ダイアログです。こちらもモーダル表示ですが、見た目は少しMac OS Xらしく変更されました。

display alertは、さまざまなオプションを指定できる一方で手軽さがいまひとつで、やはりdisplay dialog命令のほうが圧倒的に利用されてきたという経緯があります(自分もほとんどdisplay alertは使いません)。

# display alertダイアログに表示されるボタンはTouchBarに表示されますが、display dialogダイアログ上のボタンはTouchBarには表示されません

非モーダル表示なUIについては、display notificationで通知表示が使えるようになり、これで一応の解決を見たというところでしょうか。こういう動作をする通知ダイアログは、Newt On Projectの中で「どうしても欲しい」という要望が出て、2000年代初頭にすでに仲間内で作ってもらったものがあり、AppleScriptから半透明のダイアログ(画面右側からスライドアニメーション表示)を表示させていました。

その一方で、display youtubeとかdisplay tableといった、ダイアログに各種GUI部品を詰め込んで簡易表示させるインタフェース「箱庭インタフェース」が今年局所的なブームを起こし、地図は表示させるわテーブルは表示させるわ、世界地図を表示して国選択を行わせるわで「計算結果を手軽に表示できたり、ファイルパスをわかりやすく選択できて便利」という評価をいただいています。

で、この「箱庭インタフェース」はもれなくalert dialogのインタフェースを用いています。display alertで表示されるものと同じGUI部品ですね。


▲HIGにサイコーに反していろいろいじくりまくっているpickup colorダイアログ。アイコン部分を色プレビュー領域に。でも、強烈に使いやすい

ちょっとした結果表示や項目選択のためにアラートダイアログ上にGUI部品を並べるやりかたは、「アリ」だと思っています。

そんな中、知り合い筋から「alertダイアログにタイトルは出せないのか?」と問い合わせがありました。アラートダイアログでタイトル表示が可能なことは知っていましたが、

Human Interface Guidelineに合わない仕様なので、タイトル表示は使ってきませんでした。

ただ、「箱庭インタフェース」シリーズでここまで無茶苦茶をやっておいて、いまさら他人に「HIGに反する」とかいっても説得力がまったくありません。

HIGのガイドラインに反するといっても、実際に使い勝手がよくなったり、顧客や上司からの無茶振りをかわすためであればアラートダイアログにタイトルを追加するというのも、アリなのかもしれません。自分はやりませんけれども。

AppleScript名:アラートダイアログをタイトル付き表示
— Created 2019-12-26 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSAlert : a reference to current application’s NSAlert
property NSRunningApplication : a reference to current application’s NSRunningApplication

property returnCode : 0

set paramObj to {myMessage:"アラート表示", mySubMessage:"アラートダイアログを表示します", myTitle:"たいとる?"}
set dRes to displayAlert(paramObj) of me

on displayAlert(paramObj)
  my performSelectorOnMainThread:"displayAlertDialog:" withObject:(paramObj) waitUntilDone:true
  
if (my returnCode as number) = 1001 then error number -128
  
return true
end displayAlert

on displayAlertDialog:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set aTitle to myTitle of paramObj
  
  
— 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"
    
set myWin to its |window|
  end tell
  
  
myWin’s setTitle:aTitle
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
return (aScrollWithTable’s documentView’s selectedRow()) + 1
end displayAlertDialog:

on doModal:aParam
  set (my returnCode) to aParam’s runModal()
end doModal:

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy NSAlert NSRunningApplication | 2 Comments

アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択

Posted on 12月 25, 2019 by Takaaki Naganoya

アラートダイアログ上にTable Viewを表示するGUI部品を提供するAppleScriptです。Edama2さんから投稿していただいたものです。

箱庭テーブルビューシリーズは、macOS標準装備の「choose from list」コマンドでは選択肢が増えたときに使いにくいので、その代替品としてTable Viewを用いたポップアップしない単項目の選択UIを実装したものでした。


▲最初のバージョン。みなぎる使い捨て感

それが途中から「Myriad Tablesが使えない場面(Framework入りの高機能ライブラリのため、利用できないケースが発生)に使う使い捨て部品」に進化。

コードサイズの投げやりなまでの小ささと、いつもどおりのプレーンな読み味から改造のためのベースキットとして活用されまくり、派生品や亜種が内外でいろいろ発生しています(きっと知らないところで改変版が作られているはず)。

sdefをつけてライブラリ化したり、色付きダイアログなどいろいろキワモノが派生しましたが、実用性を考慮して半透明ウィンドウではなく通常ウィンドウとして表示する方向に変わりつつあります。

Version Author Date Description
v1 Piyomaru 2019/2/14 For choose from list without popup menu. One field selection UI.
v2 Piyomaru 2019/2/14
v3 Piyomaru 2019/2/14
v4 Piyomaru 2019/7/11 Table user interface for item selection
v5_e KniazidisR 2019/11/2 Separate display dialog stage into making and displaying
v5_m Piyomaru 2019/12/23 Filtering by multiple field
v6 Piyomaru 2019/12/24 Search Field Added
v7 Edama2 2019/12/240 Vertical & horizontal resizing
v8 Edama2 2019/12/25 Record sorting & Field re-ordering

以下、v8についてのedama2さんからの説明文です。

コラムのヘッダのクリックでテーブルのソート
ドラッグ&ドロップによる行の並び替え
行の複数選択

ができるようになりましたが、ヘッダのソート状態のときにD&Dの並び替えはできません。
飛び飛びに複数行選択しても並び替えできますが、たまにズレている気がします...(汗)

ArrayControllerを使用したのが機能過剰と思われたかもしれませんが、まずXCode上で一度組んだものを逆移植しているのでそんなに深い意味はないです。
でもソート方法を試行錯誤して調べていたら、Cocoa bindingをコーディングから設定できることがわかった(よく考えれば当たり前ですが)のが、何か壁を一枚破った気がしました。

Cocoa bindingがプログラムから動的に実行できることは、調査して知っていましたが、実際にAppleScriptのコードとして記述されたものは見たことがありませんでした(Mac Scripterのフォーラムを漁ると出てくるのかも?)。

### 表示内容をNSArrayControllerにバインディング
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value)

ここの部分の記述は、クリスマスプレゼントとして楽しませていただきます。Cocoa Bindingなんて、Xcode上でGUI部品とイベント受信部分のハンドラとの接続をただ漫然とヒモでつなぐ作業をしていただけだったので、Cocoa Binding Referenceなんてはじめて細部にわたって眺めましたわー。


▲起動状態。フィールドの横サイズそろえ、レコード数により表UIの縦サイズが調整されている


▲Specsフィールドを中央に持ってきた。ちゃんとUIが細かいアニメーション動作を行う


▲Priceフィールドを先頭に移動させてみた


▲Priceでソートさせてみた


▲Specsでソートさせてみた


▲選択行をドラッグ&ドロップで移動。複数行も移動可能。ただし、フィールド指定のソート状態を有効にすると行移動が効かないという


▲Commandキーを押しながら複数行選択


▲ドラッグ&ドロップで移動


▲移動後

AppleScript名:アラートダイアログ上にTable Viewを表示 v8_ソート、並び替え、複数選択.scpt
on run
  
  
set aTableList to {}
  
set aTableList’s end to {|Name|:"MacBook Air", Price:"119,800円", Specs:"1.6GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)、4MB L3キャッシュ"}
  
set aTableList’s end to {|Name|:"MacBook Pro 13", Price:"139,800円", Specs:"1.4GHzクアッドコアIntel Core i5(Turbo Boost使用時最大3.9GHz)、128MB eDRAM"}
  
set aTableList’s end to {|Name|:"MacBook Pro 15", Price:"258,800円", Specs:"2.6GHz 6コアIntel Core i7(Turbo Boost使用時最大4.5GHz)、12MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"Mac mini", Price:"122,800円", Specs:"3.0GHz 6コアIntel Core i5 Turbo Boost使用時最大4.1GHz 9MB共有L3キャッシュ"}
  
set aTableList’s end to {|Name|:"iMac 21.5", Price:"120,800円", Specs:"2.3GHzデュアルコアIntel Core i5(Turbo Boost使用時最大3.6GHz)"}
  
set aTableList’s end to {|Name|:"iMac 4K 21.5", Price:"142,800円", Specs:"3.6GHzクアッドコアIntel Core i3"}
  
set aTableList’s end to {|Name|:"iMac 5K 27", Price:"198,800円", Specs:"3.0GHz 6コア Intel Core i5(Turbo Boost使用時最大4.1GHz)"}
  
set optionRec to {aTableList:aTableList}
  
set optionRec to optionRec & {aSortOrder:{column1:"Name", column2:"Price", column3:"Specs"}}
  
  
set aMainMes to "項目の選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
set dateObj to my chooseData(aMainMes, aSubMes, optionRec)
end run

# アラートダイアログでtableviewを表示
on chooseData(aMainMes, aSubMes, optionRec)
  script MyDialog
    property parent : AppleScript
    
use AppleScript
    
use scripting additions
    
use framework "Foundation"
    
property _retrieve_data : missing value
    
on make
      set aClass to me
      
script
        property parent : aClass
      end script
    end make
    
## ダイアログの呼び出し
    
on call(aMainMes, aSubMes, optionRec)
      set paramObj to {myMessage:aMainMes, mySubMessage:aSubMes, myOption:optionRec}
      
parent’s performSelectorOnMainThread:"raize:" withObject:paramObj waitUntilDone:true
      
if (my _retrieve_data) is missing value then error number -128
      
return (my _retrieve_data)
    end call
    
## ダイアログの生成
    
on raize:paramObj
      ###
      
set mesText to paramObj’s myMessage
      
set infoText to paramObj’s mySubMessage
      
set paramObj to paramObj’s myOption
      
### set up view
      
set {theView, makeObj} to my makeContentView(paramObj)
      
### set up alert
      
tell current application’s NSAlert’s new()
        setMessageText_(mesText)
        
setInformativeText_(infoText)
        
addButtonWithTitle_("OK")
        
addButtonWithTitle_("Cancel")
        
setAccessoryView_(theView)
        
tell |window|()
          setInitialFirstResponder_(theView)
        end tell
        
#### show alert in modal loop
        
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
      end tell
      
### retrieve date
      
my retrieveData(makeObj)
    end raize:
    
    
## ContentView を作成
    
property _data_source : missing value
    
property _data_type : (current date) as text –do shell script "uuidgen" –>ユニークな文字列ならなんでもいい?
    
on makeContentView(paramObj)
      ## 準備
      
set aList to (paramObj’s aTableList) as list
      
set keyRec to (paramObj’s aSortOrder) as record
      
set keyDict to (current application’s NSDictionary’s dictionaryWithDictionary:keyRec)
      
      
set my _data_source to current application’s NSArrayController’s new()
      
repeat with anItem in aList
        set aDict to (current application’s NSDictionary’s dictionaryWithDictionary:anItem)
        ((
my _data_source)’s addObject:aDict)
      end repeat
      (
my _data_source)’s setSelectionIndex:0
      
      
## NSTableView
      
tell current application’s NSTableView’s alloc()
        tell initWithFrame_(current application’s CGRectZero)
          setAllowsEmptySelection_(false)
          
setAllowsMultipleSelection_(true)
          
setDataSource_(me)
          
setDelegate_(me)
          
setDoubleAction_("doubleAction:")
          
setGridStyleMask_(current application’s NSTableViewSolidVerticalGridLineMask)
          
setSelectionHighlightStyle_(current application’s NSTableViewSelectionHighlightStyleRegular)
          
setTarget_(me)
          
setUsesAlternatingRowBackgroundColors_(true)
          
          
registerForDraggedTypes_({my _data_type})
          
setDraggingSourceOperationMask_forLocal_(current application’s NSDragOperationCopy, false)
          
          
set thisRowHeight to rowHeight() as integer
          
set aTableObj to it
        end tell
      end tell
      
      
## NSTableColumn
      
### Columnの並び順を指定する
      
set viewWidth to 0
      
set columnsCount to keyDict’s |count|()
      
repeat with colNum from 1 to columnsCount
        
        
set keyName to "column" & colNum as text
        
set aTitle to (keyDict’s objectForKey:keyName)
        
        
tell (current application’s NSTableColumn’s alloc()’s initWithIdentifier:(colNum as text))
          tell headerCell()
            setStringValue_(aTitle)
            
set thisHeaderHeight to cellSize()’s height
          end tell
          
          
### ソートの設定
          
set sortDescriptor to (current application’s NSSortDescriptor’s sortDescriptorWithKey:aTitle ascending:true selector:"compare:")
          
setSortDescriptorPrototype_(sortDescriptor)
          
          
### 表示内容をNSArrayControllerにバインディング
          
bind_toObject_withKeyPath_options_(current application’s NSValueBinding, my _data_source, "arrangedObjects." & aTitle, missing value)
          
          
set aTableColumn to it
        end tell
        
        
### Columnの横幅を調整
        
tell aTableObj
          addTableColumn_(aTableColumn)
          
–reloadData()
          
set colWidthMax to 0
          
set rowCount to numberOfRows()
          
          
repeat with rowNum from 1 to rowCount
            tell preparedCellAtColumn_row_(colNum – 1, rowNum – 1)
              set thisWidthSize to cellSize()’s width
              
–log result
              
if thisWidthSize is greater than or equal to colWidthMax then set colWidthMax to thisWidthSize
            end tell
          end repeat
        end tell
        
–log colWidthMax
        
        
set colWidthMax to colWidthMax + 5 –> 5は余白
        
tell aTableColumn
          setWidth_(colWidthMax)
        end tell
        
        
set viewWidth to viewWidth + colWidthMax
      end repeat
      
      
## NSScrollView
      
### Viewの高さを計算
      
set viewHeight to (thisRowHeight + 2) * rowCount + thisHeaderHeight –> 2を足さないと高さが合わない
      
set vSize to current application’s NSMakeRect(0, 0, viewWidth + 10, viewHeight) –> 10を足すといい感じの横幅になる
      
      
### Viewを作成
      
tell current application’s NSScrollView’s alloc()
        tell initWithFrame_(vSize)
          setBorderType_(current application’s NSBezelBorder)
          
setDocumentView_(aTableObj)
          
–setHasHorizontalScroller_(true)
          
–setHasVerticalScroller_(true)
          
set aScroll to it
        end tell
      end tell
      
      
return {aScroll, aTableObj}
    end makeContentView
    
    
## retrieve date
    
on retrieveData(aTableObj)
      set aIndexSet to aTableObj’s selectedRowIndexes()
      
set my _retrieve_data to ((my _data_source)’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list –setSelectionIndexes:aIndexSet
      
log result
    end retrieveData
    
    
    
# NSTableViewDatasource
    
on numberOfRowsInTableView:aTableView
      –log "numberOfRowsInTableView:"
      
return (my _data_source)’s content()’s |count|()
    end numberOfRowsInTableView:
    
# NSTableViewDelegate
    
on tableView:aTableView objectValueForTableColumn:aColumn row:aRow
    
end tableView:objectValueForTableColumn:row:
    
# テーブル内のセルが編集できるか
    
on tableView:aTableView shouldEditTableColumn:aColumn row:aRow
      return false
    end tableView:shouldEditTableColumn:row:
    
# テーブル内をダブルクリックしたらOKボタンを押す
    
on doubleAction:sender
      –log "doubleAction"
      
–log sender’s |className|() as text
      
## ヘッダをクリックした時は何もしない
      
if (sender’s clickedRow()) is -1 then return
      
      
set theEvent to current application’s NSEvent’s ¬
        keyEventWithType:(current application’s NSEventTypeKeyDown) ¬
          location:(current application’s NSZeroPoint) ¬
          
modifierFlags:0 ¬
          
timestamp:0.0 ¬
          
windowNumber:(sender’s |window|()’s windowNumber()) ¬
          
context:(current application’s NSGraphicsContext’s currentContext()) ¬
          
|characters|:return ¬
          
charactersIgnoringModifiers:(missing value) ¬
          
isARepeat:false ¬
          
keyCode:0
      current application’s NSApp’s postEvent:theEvent atStart:(not false)
    end doubleAction:
    
    
# Drag Operation Method
    
## ドラッグを開始(ペーストボードに書き込む)
    
on tableView:aTableView writeRowsWithIndexes:rowIndexes toPasteboard:pboard
      –log "writeRowsWithIndexes"
      
set aData to current application’s NSKeyedArchiver’s archivedDataWithRootObject:rowIndexes
      
pboard’s declareTypes:{my _data_type} owner:(missing value)
      
pboard’s setData:aData forType:(my _data_type)
      
return true
    end tableView:writeRowsWithIndexes:toPasteboard:
    
## ドラッグ途中
    
on tableView:aTableView validateDrop:info proposedRow:row proposedDropOperation:operation
      –log "validateDrop"
      
### 列の間にドラッグ
      
if (operation is current application’s NSTableViewDropAbove) then
        return current application’s NSDragOperationMove
      end if
      
### 項目の上にドラッグ
      
set aTypes to info’s draggingPasteboard’s types()
      
if (aTypes’s containsObject:(my _data_type)) as boolean then
        return current application’s NSDragOperationNone
      end if
      
return current application’s NSDragOperationNone
    end tableView:validateDrop:proposedRow:proposedDropOperation:
    
## ドラッグ終了
    
on tableView:aTableView acceptDrop:info row:row dropOperation:operation
      –log "acceptDrop"
      
set pboard to info’s draggingPasteboard()
      
      
set rowData to pboard’s dataForType:(my _data_type)
      
set rowIndexes to current application’s NSKeyedUnarchiver’s unarchiveObjectWithData:rowData
      
      
if (rowIndexes’s firstIndex()) < row then
        set row to row – (rowIndexes’s |count|())
      end if
      
      
set aRange to current application’s NSMakeRange(row, rowIndexes’s |count|())
      
set aIndexSet to current application’s NSIndexSet’s indexSetWithIndexesInRange:aRange
      
      
tell my _data_source
        set anObj to content()’s objectsAtIndexes:rowIndexes
        
removeObjects_(anObj)
        
insertObjects_atArrangedObjectIndexes_(anObj, aIndexSet)
        
rearrangeObjects()
      end tell
      
return true
    end tableView:acceptDrop:row:dropOperation:
  end script
  
  
##
  
tell (make MyDialog)
    return call(aMainMes, aSubMes, optionRec)
  end tell
end chooseData

★Click Here to Open This Script 

Posted in dialog GUI | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

Post navigation

  • Older posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • macOS 13.6.5 AS系のバグ、一切直らず
  • CotEditorで2つの書類の行単位での差分検出
  • Apple純正マウス、キーボードのバッテリー残量取得
  • macOS 15, Sequoia
  • ディスプレイをスリープ状態にして処理続行
  • 初心者がつまづきやすい「log」コマンド
  • Adobe AcrobatをAppleScriptから操作してPDF圧縮
  • 与えられた文字列の1D Listのすべての順列組み合わせパターン文字列を返す v3(ベンチマーク用)
  • メキシカンハットの描画
  • macOS 14、英語環境で12時間表記文字と時刻の間に不可視スペースを入れる仕様に
  • macOS 13 TTS環境の変化について
  • 新刊発売 AppleScript最新リファレンス v2.8対応
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • 2023年に書いた価値あるAppleScript
  • 指定のWordファイルをPDFに書き出す
  • 可変次元のベクトルに対応したコサイン類似度計算
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • macOS 13.6.2アップデート Cocoa-AppleScript Applet修正はなし

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (586) 10.15savvy (437) 11.0savvy (281) 12.0savvy (201) 13.0savvy (128) 14.0savvy (76) 15.0savvy (48) CotEditor (62) Finder (50) iTunes (19) Keynote (110) NSAlert (60) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (51) NSDictionary (27) NSFileManager (23) NSFont (19) NSImage (41) NSJSONSerialization (21) NSMutableArray (62) NSMutableDictionary (21) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (117) NSURL (97) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (65) Pages (50) Safari (44) Script Editor (25) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • Clipboard
  • Code Sign
  • Color
  • Custom Class
  • dialog
  • diff
  • drive
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC