HexLauncher: быстрый запуск приложений в Mac OS X
История создания данного продукта возвращает меня на 5 лет назад во времена, когда я только начал входить в мир Apple технологий, а именно — мигрировать на операционную систему Mac OS X. Как и для многих пенреход был связан с некоторыми неудобставми из-за отсутствия привычных программ и элементов управления системой. Одним из недоразуменей стало отсутствие аналога кнопки Пуск для запуска программ путем выбора из списка. Честно говоря, до сих пор не уверен, что я правильно запускаю программы в OS X (для этого я использую Spotlight). И вот, спустя несколько лет я решил устранить этот недостаток.
Надо сказать, я неоднократно пытался найти готовое решение данной проблемы. Но все, что я находил по той или иной причине не устраивало меня. И это в очередной раз привело меня к сакральной истине: если хочешь что-то сделать, сделай это сам. Да будет так!
Вооружившись Xcode’ом и поисковыми средствами интернета я приступил к разработке. Кстати, забегая немного вперед, скачать HexLauncher можно здесь.
Тех задание было следующим. Требуется разработать приложение, которое при запуске помещало бы в системное меню иконку, при нажатии на которую появлялось бы меню со списком избранных приложений, выбрав одно из которых можно было осуществить быстрый запуск. Данный способ гораздо более удобен с точки зрения юзабилити, нежели Spotlight, хотя бы из тех соображений, что при использовании Spotlight нужно использовать и мышь и клавиатуру. HexLauncher же позволяет запускать приложение в два клика. Список приложений в меню формируется с помощью окна настроек, вызов которого так же доступен из меню программы. При открытии окна настроек в него загружаются иконки и названия всех приложений, находящихся в папке /Applications — это главное хранилище приложений в платформе Mac OS X. Пользователь может отметить галками желаемые приложения, и они будут отображаться в выпадающем меню. Так же необходимо предоставить пользователю возможность запуска программы при старте системы. Вот такое незамысловатое ТЗ. Что ж, приступим.
Мало-мальски сложным в данном проекте для меня стали несколько вещей.
1. Собственно, поместить иконку в системное меню Mac OS X и ассоциировать с ним меню. Не буду долго теоретизировать, просто приведу кусок кода, который родился после средней продолжительности гугления.
- (void)setupMenu { NSMenu *menu = [[NSMenu alloc] init]; // for (NSString * app in self.apps) { NSMenuItem * item = [menu addItemWithTitle:@"HexLauncher Preferences" action:@selector(setup:) keyEquivalent:@""]; NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[[NSBundle mainBundle] bundlePath]]; item.image = iconImage; [menu addItem:[NSMenuItem separatorItem]]; int count = 0; for (int i = 0; i < self.apps.count; i++) { AppItem * app = self.apps[i]; if (!app.checked) continue; count++; NSMenuItem * item = [menu addItemWithTitle:[app.appTitle substringWithRange:NSMakeRange(0, app.appTitle.length - 4)] action:@selector(launchApp:) keyEquivalent:@""]; item.tag = i; item.image = app.icon; } if (count) [menu addItem:[NSMenuItem separatorItem]]; [menu addItemWithTitle:@"Quit HexLauncher" action:@selector(terminate:) keyEquivalent:@""]; self.statusItem.menu = menu; } - (void)setupStatusItem { _statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; _statusItem.title = @""; _statusItem.image = [NSImage imageNamed:@"StatusItem-Image-rocket"]; _statusItem.alternateImage = [NSImage imageNamed:@"StatusItem-Image-rocket-alt"]; _statusItem.highlightMode = YES; [self gatherAppsList]; [self setupMenu]; }
Думаю, тут все понятно. systemMenu — объект класса NSStatusItem, он собственно и делает всю магию с иконкой в системном меню. Метод gatherAppsList сканирует рекурсивно директорию /Applications и выдает массив, из которого и формируется меню.
2. Следующим сложным моментом стало отображение в списке приложений иконок. Еще когда я создавал свое первое приложение для OS X (MacMines) я столкнулся с этим хитрым форматом icns (IconSet), который содержит сет из иконок разного разрешения. Очень долго искал, как можно с ним работать, и как, грубо говоря, конвертировать ICNS в NSImage. Из чего-то более-менее вразумительного попался какой-то opensource-проект написанный на c++. К счастью, удачный поисковый запрос выдал мне гениально-простое решение и избавил тем самым от возни с сорцами на плюсах. Оказывается, в Cocoa-фреймворке существуют стредства для получения готового изображения для любого приложения. Вот как это выглядит.
NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
где appPath, как нетрудно догадаться, путь к приложению (например, /Applications/Skype.app)
3. Запуск приложения Mac OS X из другого приложения так же не оказался чем-то криминальным (у меня были опасения, что возможно будут какие-то трудности связанные с безопасностью)
NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace]; BOOL didLaunch = [sharedWorkspace launchApplication:app.appPath];
4. Интересной окалась задача показать окно настроек поверх других — то есть, вынести на самый верх окно программы.
- (void)setup:(id)sender { [self.mainWindowController.window makeKeyAndOrderFront:self]; [NSApp activateIgnoringOtherApps:YES]; }
5. Самой, пожалуй, сложной для реализации стала функциональность запуска приложения при старте (Launch app on start up). Самый распространенный способ, используемый большинством прикладных приложений, является так называемые Launch agents. Подробно об агентах запуска в Mac OS X можно ознакомиться здесь. Я лишь расскажу в кратце, как это действует. Агент запуска создается с помощью обычного Plist, содержащего информацию о запускаемом приложении, а так же о правилах запуска (это такой продвинутый cron-планировщик по сути). Вот, как выглядит агент запуска для моего приложения
<?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>Label</key> <string>ru.heximal.HexLauncher</string> <key>ProgramArguments</key> <array> <string>%APP_DIR%/Contents/MacOS/HexLauncher</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>
Думаю, комментарии тут излишни. Скажу лишь, что %APP_DIR% — это placeholder. Этот файл лежит в бандле проекта, и плэйсхолдер %APP_DIR% подменяется строкой, которая содержит путь к папке, где лежит само приложение HexLauncher. Вот, кстати, как получить путь в папке Mac OS X приложения:
NSBundle *myBundle = [NSBundle mainBundle]; NSString * bundlePath = [myBundle bundlePath];
Также про агентов запуска можно добавить следующее. Они могут находиться либо в домашней директории пользоватля
~/Library/LaunchAgents/
либо в глобальной библиотеке
/Library/LaunchAgents/
Как не трудно догадаться, в первом случае агент будет работать только для того самого пользователя, а во втором — для любого пользователя. Вот, пожалуй, и все на сегодня.
То есть это, по сути, список любимых и самых важных программ?
А чем плох док?
И еще такая мысль пришла: запустить все свои приложения, которыми пользуешься в рабочей среде, нужно только раз — затем мак просто засыпает, и просыпается на той же ноте..)
Да, утечки существуют, но они накапливаются месяцами…
Привет. Да, почти док) но док тоже немного мозг выносит, не информативен за счет того, что непонятно, запущено в данный момент приложение или нет. ну то есть, конечно понятно, там ведь рядом точечка есть, если приложение активно. но бесит, когда случайно тыкаешь иконку в доке, которую не хотел, и при этом запускается какой-то нежелательный апп. особенно, если этот апп тяжелый. поэтому я предпочитаю по минимуму в доке закрепленных аппов держать, в большинстве только те, которые почти всегда запущены) вот для всего остального этот чудесный лончер)
Есть еще launchpad — но тоже как-то я не проникся им.
сори за долгий тормоз, после переезда на новый серв как-то почта нестабильно ходит)
Здравствуйте!
Скачал HexLauncher и у меня возникла проблема.
На странице скачивания написано Requires Mac OS X 10.8 or later
Во время установки на 10.9.5 (хак) вышло сообщение, что требуется OS X 10.10
и никак иначе…
P.S.
Спасибо за скринсэйвер «матрица» — обожаю его! Вот бы такой под макось (тамошний верик не такой красивший, как Ваш))
Здравствуйте.
упс, действительно, косяк, забыл выставить минимальный таргет в настройках проекта, в ближайшее время пересоберу и отпишусь сюда)
Спасибо за отзыв, очень приятно.
Матрица у меня была портирована под мак, и даже работала на 10.5 (Leopard).
А в следующей версии 10.6 (Lion) Apple ужесточила политику безопасности относительно экранных заставок сторонних производителей, и у меня не хватило терпения разобраться, как это победить)
Может когда-нить дойдут и до этого руки)
Ещё раз здравствуйте! ))
Я прошу прощения за свою назойливость — просто хочу узнать, Вы ещё не пересобрали HexLauncher? Или вдруг по прошествии (почти) месяца запамятовали…
Мы как бы ждём-с :-)
Мильон извинений, дела-дела, суматошное девелоперское бытиё…
Исправляюсь.
Выложил билд 1.1
http://heximal.ru/download/hexlauncher/HexLauncher_1.1.dmg
Буду премного признателен за тестирование на OS X 10.8
Собираюсь опубликовать в качестве первого mac-приложение в Mac AppStore
Отлично, спасибо!
У меня самого в распоряжении есть только 10.6.8 и 10.9.6. — однако, мой знакомый с 10.8 уже при деле ))
Будем тестить.
Если что найдём, я Вам непременно напишу.
Итак, сейчас ситуация такая.
После запуска на осях 10.8.5 и 10.9.5 лаунчер появляется в процессах, но иконки его сверху нет (не подаёт признаков жизни).
В 10.11.1 GM запустился успешно, заработал.
На других версиях OS X мы пока не можем проверить приложение.