MacBookのあらゆるウィンドウをキー操作で自在に操るために(AppleScript + Quicksilver)
MacOSXのバージョンも10.5.3になり着々と洗練されてきたが、ウィンドウ操作だけはどうしても不満が残る。この不満はOSXになって以来、自分の中でずっと続いている。
キー操作またはマウスクリック一発で、ウィンドウを画面いっぱいまで最大化する方法がないこと。
確か...OS9の頃は「オプションキー + ズームボタンクリック」で画面いっぱいまで最大化できた記憶がある。(かなりのアプリケーションが対応してくれていた気がする。そして、もう一度押すと直前のウィンドウサイズに戻る。この繰り返しが非常に使い勝手が良かった。)自分の経験では、ノートパソコンの狭い画面では、常にウィンドウを最大化したい欲求があり、ドラッグ&ドロップの操作の時だけ一時的にウィンドウを小さくするような使い方が多い。ExposeやSpacesが利用できる今の環境では、最大化しておきたい欲求はさらに高まる。
- それなのにズームボタンを押しても、Safariのウィンドウは逆に小さくなるし、もう一度押しても元のサイズに戻りさえしない...。
- Firefox2の方はまだマシだが、最大化した時は右側に128ドットの隙間を残す。おそらくこの仕様は、ダウンロードしたファイル等をマウスで操作しやすい親切設計なのだろうけど、Dockのスタック表示あり、ダウンロードフォルダあり、ExposeやSpacesありの今となっては、無用なおせっかい機能な気がする。(Firefox3では目一杯最大化するようになった。)
- Finderのカラム表示では今のウィンドウの高さを維持したまま、横幅だけ画面いっぱいにしたいことが多々ある。それなのに多くの場合、この時だけは画面目一杯に最大化されることが多い。(自分の勝手で申し訳ないが、Finderではドラッグ&ドロップの操作が多いので、最大化して欲しくないのだ。)
プレビューバージョンを含めればOSXになってから6世代この状態なのだから、OS側のサポートにはもう期待しない。Rubiscoという素晴らしいソフトウェアもあるのだが、Cocoaアプリケーションのみ対応。よって、Finder、Firefox、iTunesなどは操作できないそうだ。残念...。
こうなったら、いつものごとく、困ったときの最後の手段、AppleScriptでゴニョゴニョ...試行錯誤してみた。そうそう、GUIスクリプティングがあるさ、AppleScript対応に関係なく、GUIをOSレベルで直接操作できるのだから、理論的にはすべてのウィンドウを操れることになってるのだから...。
ウィンドウを最大化する
まずはウィンドウを最大化するスクリプトを書いてみた。アプリケーションのAppleScript対応状況に関係なく操作できるようにしたいので、GUIスクリプティングを利用して操作することに。だから、システム環境設定のユニバーサルアクセスの「補助装置にアクセスできるようにする」をチェックありの状態にしないと、以下のスクリプトは機能しない。
---------- zoom_window_full.scpt ---------- --最前面のアプリケーションを取得する on frontmost_app() tell application "System Events" set pList to name of every process whose frontmost is true set appName to item 1 of pList --"Script Editor" --set appName to name of (path to frontmost application)--"Script Editor.app" end tell --return appName--最後に評価された値が戻り値になるので不要 end frontmost_app on run --デスクトッップのサイズを取得 tell application "Finder" set menuHight to 22 set displayBounds to bounds of window of desktop set displayPosition to {item 1 of displayBounds, (item 2 of displayBounds) + menuHight} set displaySize to {item 3 of displayBounds, (item 4 of displayBounds) - menuHight} end tell set appName to frontmost_app() tell application "System Events" tell process appName set topWindow to window 1 set position of topWindow to displayPosition set size of topWindow to displaySize end tell end tell end run
元のサイズ・ポシションに戻す
最大化するようになったが、これで満足してはいけない。やはり、同じ操作(スクリプト)を繰り返し実行した時は、元のウィンドウサイズ・ポジションに戻したい。プロパティを利用して以下のようにしてみた。
---------- zoom_window_full.scpt ---------- property lastName : "" property lastPosition : {} property lastSize : {} --最前面のアプリケーションを取得する on frontmost_app() tell application "System Events" set pList to name of every process whose frontmost is true set appName to item 1 of pList --"Script Editor" --set appName to name of (path to frontmost application)--"Script Editor.app" end tell --return appName--最後に評価された値が戻り値になるので不要 end frontmost_app on run --デスクトッップのサイズを取得 tell application "Finder" set menuHight to 22 set displayBounds to bounds of window of desktop set displayPosition to {item 1 of displayBounds, (item 2 of displayBounds) + menuHight} set displaySize to {item 3 of displayBounds, (item 4 of displayBounds) - menuHight} end tell set appName to frontmost_app() tell application "System Events" tell process appName set topWindow to window 1 set topName to name of topWindow if topName is lastName then set lastName to "" set position of topWindow to lastPosition set size of topWindow to lastSize else set lastName to topName set lastPosition to position of topWindow set lastSize to size of topWindow set position of topWindow to displayPosition set size of topWindow to displaySize end if end tell end tell end run
これでウィンドウが、最大化と直前の状態を繰り返すようになった!
キー操作でスクリプトを呼び出す
上記スクリプトは、フォーマット: スクリプトで「~/Library/Scripts/」に保存すれば、メニューバーのスクリプトメニュー*1から利用できる。
- Dockに登録してもフォーマット: スクリプトなので、スクリプトエディタで編集画面が開いてしまう。
- そうするとフォーマット: アプリケーションで保存したくなるが、最前面のアプリケーションに対してのウィンドウ操作になるので、自分自身が対象になってしまいエラーが発生する。
- AppleScriptを利用する時、一般的にフォーマット: スクリプトの方が、アプリケーションとして実行するよりもキビキビ動く気がする。
そしてもう一つ、素晴らしい実行方法がある。QuicksilverのTriggersにキーボードショットカットとして登録してしまうのだ。自分の場合は「ctrl + option + z」を割り当てた。これでウィンドウを最大化したくなった時、キー操作一発で瞬時に最大化する!もう一度同じキー操作をすれば、元の位置、大きさに戻る。これでちょっと満足。
Quicksilverについて
- Quicksilverについては、あまりにも有名で奥深いソフトウェアなので、google:Quicksilver 使い方で、かなりの数の素晴らしい解説ページがヒットする。
- 中でもわかばマークのMacの備忘録さんの解説記事にはたいへんお世話になっています。感謝です!
- ちなみに、自分がMacBookにインストールしているバージョンは3815となっている。(QS.3815.dmgをダウンロードしてインストールした。)
ウィンドウを微妙にズラすために
面白くなってウィンドウをばしばし最大化していると、ちょっとだけ困った現象に遭遇した。例えば、4つのウィンドウを最大化してExposeで一覧表示してみると...
このように横1列に表示されてしまう...。確かに一覧表示にはなっているけど、スペースの使い方が今イチな感じだ。本来は以下のように並んでくれると嬉しい。
Exposeがどのようなルールでウィンドウを並べているかは理解できなかったが、全く同じ位置と大きさのウィンドウを並べる時に横一列の状況になってしまうようだ。解決策は簡単で、一つのウィンドウだけ、1ドットでもいいから他のウィンドウと異なるように縦横方向にズラしてあげること。ウィンドウを最大化するスクリプトで自動化することも考えたが、複数のウィンドウを管理するのが面倒だ。よって、最大化のスクリプトと同じように一つのスクリプトとして独立させ、困った時だけキーボードショットカットでズラしの操作をすることにした。
---------- move_window_1px.scpt ---------- --最前面のアプリケーションを取得する on frontmost_app() tell application "System Events" set pList to name of every process whose frontmost is true item 1 of pList --"Script Editor" end tell end frontmost_app on run set appName to frontmost_app() tell application "System Events" tell process appName set topWindow to window 1 set topPosition to position of topWindow --Spacesで横一列、または縦一列の配置防止のため1ドットのズレを作る set position of topWindow to {(item 1 of topPosition) + 1, (item 2 of topPosition) + 1} end tell end tell end run
上記スクリプトをスクリプトメニューに登録して、Quicksilverでキーボードショットカットは「ctrl + option + x」を割り当てた。これでスペースを無駄なく利用できるようになった!
ウィンドウを操作するスクリプトいろいろ
ウィンドウを操作するスクリプトの作り方は分かってきたので、あとは想像力と試行錯誤で、自分好みの操作を実現するスクリプトを作って登録するだけだ。その後、好みで追加したスクリプトは以下。
- ウィンドウを上下左右に寄せる。
- jump_window_down.scpt
- jump_window_left.scpt
- jump_window_right.scpt
- jump_window_up.scpt
- ウィンドウを少しずつ移動する。
- move_window_1px.scpt(Exposeで最適な配置にするために利用)
- move_window_down.scpt
- move_window_left.scpt
- move_window_right.scpt
- move_window_up.scpt
- ウィンドウのサイズを少しずつ変更する。
- resize_window_high.scpt(高くする)
- resize_window_low.scpt(低くする)
- resize_window_narrow.scpt(狭める)
- resize_window_wide.scpt(広げる)
- ウィンドウの位置とサイズを一気に変更する。
- zoom_window_default.scpt(Finderのデフォルトサイズ750×425にする)
- zoom_window_full_hight.scpt(高さだけ画面いっぱいにする)
- zoom_window_full_width.scpt(幅だけ画面いっぱいにする)
- zoom_window_full.scpt(画面いっぱいにする)
- zoom_window_half_hight.scpt(高さだけ画面の半分にする)
- zoom_window_half_width.scpt(幅だけ画面の半分にする)
上記スクリプトを作っている過程で、今までの1話完結のスクリプト方式では重複箇所が非常に多くなることに気付く。Railsから学んだ徹底したDRYの原則に大きく反する。しかし、Rubyのようにブロックを引数にする仕組みは無さそうなので、仕方なく以下のように書いてみた。
---------- zoom_window_full.scpt ---------- property windowBase : load script file (((path to scripts folder) as text) & "window_operation:window_base.scpt") on run operate_window("full_size") of windowBase end run
---------- window_base.scpt ---------- property lastWindow : load script file (((path to scripts folder) as text) & "window_operation:window_pref.scpt") --最前面のアプリケーションを取得する on frontmost_app() tell application "System Events" set pList to name of every process whose frontmost is true item 1 of pList end tell end frontmost_app --ウィンドウを操作する on operate_window(op) --デスクトッップのサイズを取得 tell application "Finder" set menuHight to 22 set displayBounds to bounds of window of desktop set displayPosition to {item 1 of displayBounds, (item 2 of displayBounds) + menuHight} set displaySize to {item 3 of displayBounds, (item 4 of displayBounds) - menuHight} set displayHalfSize to {(item 1 of displaySize) / 2, (item 2 of displaySize) / 2} set displayFinderSize to {750, 425} set px to 20 end tell set appName to frontmost_app() tell application "System Events" tell process appName load_pref() of lastWindow --前回のウィンドウ状態を取得 set topWindow to front window --window 1と同等 set storeName to name of topWindow & op set storeposition to position of topWindow set storeSize to size of topWindow if storeName is _name() of lastWindow then --以前のwindow配置を設定 set afterPosition to _position() of lastWindow set afterSize to _size() of lastWindow set_name("") of lastWindow else --スクリーンに合わせたwindow配置に設定 if op is "full_size" then set afterPosition to displayPosition set afterSize to displaySize set_name(storeName) of lastWindow else if op is "full_width" then set afterPosition to {item 1 of displayPosition, item 2 of storeposition} set afterSize to {item 1 of displaySize, item 2 of storeSize} set_name(storeName) of lastWindow else if op is "full_height" then set afterPosition to {item 1 of storeposition, item 2 of displayPosition} set afterSize to {item 1 of storeSize, item 2 of displaySize} set_name(storeName) of lastWindow else if op is "half_width" then --(中略、ウィンドウ操作の数だけifが続く)-- end if end if set position of topWindow to afterPosition set size of topWindow to afterSize set_position(storeposition) of lastWindow set_size(storeSize) of lastWindow save_pref() of lastWindow --ウィンドウ状態を保存する end tell end tell end operate_window
AppleScriptを別ファイルに保存して、それをスクリプトオブジェクトとして呼び出して実行した時に、プロパティが思うように更新されなかった。悩んでみたが対策分からず...。しょうがなく自分でファイル操作をして、環境設定ファイルに保存するようにしてみた。
---------- window_pref.scpt ---------- property lastName : "" property lastPosition : {} property lastSize : {} on pref_path() ((path to preferences folder) as text) & "com.bebekoubou.window_operation_pref.csv" end pref_path on save_pref() set prefList to lastPosition & lastSize set prefText to lastName repeat with prefItem in prefList set prefText to prefText & "," & (prefItem as text) end repeat try set f to open for access file pref_path() with write permission set eof f to 0 write prefText to f end try close access f end save_pref on load_pref() try set f to open for access file pref_path() set prefList to read f using delimiter {","} as text on error set prefList to {"", "", "", "", ""} end try close access f prefList set lastName to item 1 of prefList set lastPosition to {item 2 of prefList, item 3 of prefList} set lastSize to {item 4 of prefList, item 5 of prefList} end load_pref on _name() lastName end _name on set_name(aName) set lastName to aName end set_name on _position() lastPosition end _position on set_position(aPosition) set lastPosition to aPosition end set_position on _size() lastSize end _size on set_size(aSize) set lastSize to aSize end set_size
- 「~/Library/Scripts/window_operation*2」というフォルダを作って、すべてのスクリプトはそこに登録した。
- QuicksilverのTriggers設定は、自分では以下のようにしてみた。
以上で作業完了。これで自分のウィンドウ操作にまつわる不満は、かなり解消した!Quicksilverによるキー操作でのAppleScript呼び出しは、予想以上に快適だ!
解決できない問題
- フローティングウィンドウがあると、編集中のメインウィンドウよりも優先して操作対象になってしまい、肝心のメインウィンドウが操作できない。
- その場合の対応策としては、今のところフローティングウィンドウを閉じるしかない。(それならマウスで操作した方が早いじゃないかという感じ)
ダウンロード
環境
- MacBook OSX 10.5.3環境で作成。同じ環境なら問題なく利用できると思います。(ちなみにPowerBookG4 OSX 10.4.11環境では動作しませんでした。)
*1:「/Applications/AppleScript/AppleScript ユーティリティ.app」を起動して、「メニューバーにスクリプトメニューを表示」をチェックありに。この設定画面でGUIスクリプティングを有効にすることも可能。