Сворачивание приложений в Dock для ленивых с помощью AppleScript

от автора

Как часто вы пользуетесь опциями некоторых программ (iTerm 2, Total Finder, Adium), которые позволяют показать окно приложения по нажатию на глобальный хоткей и скрыть это приложение при потере фокуса? Лично я — постоянно. А что если некая программа не имеет такого функционала и постоянно маячит перед глазами? Тот же Skype, например. Под катом вариант приведения своего рабочего пространства в порядок.

Открываем и скрываем приложение по хоткею

Начнем с самого простого. Задача: по нажатию на глобальный хоткей из любой программы передать фокус Skype и этим же действием его скрыть. Существует целый парк программ, которые позволяют сделать это очень просто. Однако мы не ищем простых путей и не готовы устанавливать дополнительные программки для этих целей. В данном случае на помощь приходит Automator с возможностью создать сервис и повесить хоткей на его вызов из любого приложения.

Запускаем Automator, выбираем тип документа Служба. В списке Служба получает выбираем нет входных данных в соседнем списке оставляем в любой программе. Добавляем в нашу службу действие Запустить AppleScript и вставляем следующий код:

set appName to "Skype" set startIt to false tell application "System Events"     if not (exists process appName) then         set startIt to true     else if frontmost of process appName then         set visible of process appName to false     else         set frontmost of process appName to true     end if end tell if startIt then     tell application appName to activate end if 

Как это выглядит

Данный код честно позаимствован отсюда.
Здесь все просто: скрипт проверяет, запущено ли приложение, если нет, то запускает его, иначе либо сворачивает его (если окно приложения находится в фокусе), либо отдает ему фокус (если в данный момент активно другое приложение).

Сохраняем нашу службу, переходим в Системные настройки > Клавиатура > Сочетание клавиш в списке слева выбираем Службы, в списке справа находим только что созданную нами службу и привязываем к ней хоткей. Цель достигнута, однако без одного но здесь не обойдется.

Проблема в том, что вызов службы не привязывается к глобальному хоткею. Службы вызываются из каждого приложения «локально». Например, находясь в данный момент в Safari, в меню программы мы найдем подменю Службы (т.е. Safari > Службы), где нам будет доступна наша служба для запуска. Соответственно запустить ее мы можем вручную из любого приложения, а назначенный нами хоткей имеет меньший приоритет по сравнению с настройками конкретного приложения. Отсюда имеем два выхода: либо назначить для вызова нашей службы сочетание клавиш, которое не используется больше нигде в системе, ни в одной программе, либо прибегнуть к помощи тех самых сторонних программ, которые дадут возможность повесить глобальный хоткей на вызов службы. Лично я пошел по третьему пути и воспользовался возможностями Alfred, который позволяет не прибегать к выше описанным действиям и дает возможность показывать и скрывать приложение по нажатию на заданный глобальный хоткей.

Что там с автохайдингом?

Итак, мы научились показывать и скрывать приложение по нажатию на глобальный хоткей, но жать на кнопку, прежде чем переключиться в другое приложение, напряжно. Решим и эту проблему.

Открываем Редактор AppleScript и вставляем следующий код:

property appName : "Skype"  on idle     tell application "System Events"         set focusedApp to (name of the first process whose frontmost is true)         if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then             set visible of process appName to false         end if     end tell     return 0.5 end idle 

Жмем Файл > Сохранить, выбираем формат файлаПрограмма, ставим галку рядом с Оставлять открытым после запуска обработчика, сохраняем программу. После этого в редакторе доступна кнопка Содержание пакета, нажав на нее, справа выползет окно, в котором ищем кнопку с шестерней, в появившемся меню выбираем Показать в Finder:

Как это выглядит

В открывшейся папке будет лежать файл Info.plist, открываем его в текстовом редакторе, после четвертой строки вставляем:

<key>LSBackgroundOnly</key> <string>1</string> 
Вот, что должно получиться

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict>     <key>LSBackgroundOnly</key>     <string>1</string>     <key>CFBundleAllowMixedLocalizations</key>     <true/>     <key>CFBundleDevelopmentRegion</key>     <string>English</string>     <key>CFBundleExecutable</key>     <string>applet</string>     <key>CFBundleIconFile</key>     <string>applet</string>     <key>CFBundleIdentifier</key>     <string>com.apple.ScriptEditor.id.HideSkype</string>     <key>CFBundleInfoDictionaryVersion</key>     <string>6.0</string>     <key>CFBundleName</key>     <string>HideSkype</string>     <key>CFBundlePackageType</key>     <string>APPL</string>     <key>CFBundleShortVersionString</key>     <string>1.0</string>     <key>CFBundleSignature</key>     <string>aplt</string>     <key>LSMinimumSystemVersionByArchitecture</key>     <dict>         <key>x86_64</key>         <string>10.6</string>     </dict>     <key>LSRequiresCarbon</key>     <true/>     <key>WindowState</key>     <dict>         <key>dividerCollapsed</key>         <false/>         <key>eventLogLevel</key>         <integer>2</integer>         <key>name</key>         <string>ScriptWindowState</string>         <key>positionOfDivider</key>         <real>333</real>         <key>savedFrame</key>         <string>55 281 602 597 0 0 1440 878 </string>         <key>selectedTabView</key>         <string>event log</string>     </dict> </dict> </plist> 

Сохраняем файл.

Вот и все, запускаем наше приложение, добавляем в автозагрузку и радуемся полученному результату. Отныне скайп глаза не мозолит.
Если вам интересны особенности реализации, то читаем дальше.

Разбор полетов

AppleScript для меня оказался открытием, так что буквально пару дней назад я и не знал о существовании этого «чуда» (уж очень он смахивает на язык для домохозяек). Зато предоставляет достаточно возможностей для автоматизации процессов в вашем рабочем окружении. Для выше описанных действий не понадобилась установка инструментов разработчика, а учитывая полученный результат, можно говорить о наличии мощного инструмента, который всегда под рукой.

Попробую разложить по полочкам решение задачи про автоскрытию приложения.

Сначала научимся определять, находится ли конкретное приложение в фокусе, или нет:

set appName to "Skype" --думаю, комментарии не уместны tell application "System Events" --начинаем работу с объектом application, в данном случае с программой System Events     set focusedApp to (name of the first process whose frontmost is true) --записываем в focusedApp имя приложения, которое в данный момент в фокусе     if focusedApp is appName then         --вот и выяснили, что приложение с именем appName находится в фокусе     end if end tell 

Очень непривычно читать такой код, зато приятно — сомодокументированный код.

Если фокус приложения потерян, то скрываем его:

set appName to "Skype" tell application "System Events"     set focusedApp to (name of the first process whose frontmost is true)     if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then         set visible of process appName to false     end if end tell 

Вот и решение задачи. Осталось завернуть его в бесконечный цикл.

set appName to "Skype" repeat while true     tell application "System Events"         set focusedApp to (name of the first process whose frontmost is true)         if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then             set visible of process appName to false         end if     end tell     delay 0.5 --делаем задержку в пол секунды перед следующей итерацией end repeat 

И это уже рабочее решение. Однако если мы оставим его в таком виде, то скрипт просто-напросто зависнет, хоть и будет работать корректно. Решение — вынести цикл в отдельный поток. Но увы, потоков в AppleScript нет. Идем в документацию и открываем для себя хэндлер idle. Хэндлерами в AppleScript называются, по сути, привычные нам процедуры. Особенность хэндлера idle в том, что он вызывается системой каждые 30 секунд, если не возвращает никакого значения, если же он вернет, допустим, 5, то будет вызываться каждые 5 секунд.

on idle     set appName to "Skype"     tell application "System Events"         set focusedApp to (name of the first process whose frontmost is true)         if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then             set visible of process appName to false         end if     end tell     return 0.5 end idle 

Также можно отказаться от использования idle и работать с хендлерами run и quit:

property appName: "Skype" property running: true  on run     repeat while running         tell application "System Events"             set focusedApp to (name of the first process whose frontmost is true)             if (focusedApp is not appName) and (exists process appName) and (visible of process appName) then                 set visible of process appName to false             end if         end tell         delay 1     end repeat end run  on quit     set running to false end quit 

Хочу один обработчик для нескольких программ

Да без проблем:

property appsToHide: {"Skype", "Adium", "Sublime Text 2"}  on idle     tell application "System Events"         set focusedApp to name of the first process whose frontmost is true         repeat with appToHide in appsToHide             if (focusedApp is not in appToHide) and (exists process appToHide) and (visible of process appToHide) then                 set visible of process appToHide to false             end if         end repeat     end tell     return 0.5 end idle 

Стоит отметить следующий момент: focusedApp is not in appToHide. Здесь проверяется, содержится ли значение focusedApp в appToHide, хотя правильно бы было просто сравнить строки (операторы =, is equal, equals, [is] equal to, is not isn't, isn't equal [to], is not equal [to], doesn't equal, does not equal). Но в данном примере строки (тип данных text) так и не захотели корректно сравниваться.

На этом я заканчиваю свой рассказ. Много подробностей об AppleScript можно найти в официальной документации. Если кто сомневается в актуальности данного инструмента, то посмотрите в release notes — возможности языка расширяют с каждым релизом OS X. Также поисковики пестрят уже готовыми howto по решению задач с помощью AppleScript.

Спасибо за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/202864/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *