Абстрагируемся от горячих клавиш в десктопных приложениях, или как отлаживаться в любом IDE одними и теми же кнопками

от автора

При работе со многими программами использование разнообразных комбинаций горячих клавиш — залог высокой производительности и удобства для пользователя.
Для достаточно сложных программ мы заучиваем десятки специфических комбинаций клавиш для различный действий.
Это позволяет сосредоточиться на фактической работе, а не на блуждании мышкой по многоэтажным меню в поисках необходимого пункта.

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

Это хорошо, однако у данного интерфейса пользовательского ввода есть проблемы.

Давайте рассмотрим их на примере работы в знакомой большинству обитателей хабра среде — интегрированной среде разработки (IDE).

Это сложная программная система, имеющая множество аспектов использования (написание кода, навигация по коду, рефакторинг, компиляция и исполнение, отладка, работа с системой контроля версий, и.т.д).

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

Первая проблема — забываются нерегулярно используемые комбинации.

Первая проблема заключается в том, что если какое-либо действие делается нерегулярно, то соответствующая комбинация клавиш с большой вероятностью забудется к следующему разу её использования. При этом, в тот момент, когда эта комбинация снова станет нужна, она может оказаться использованной не один-два раза, а достаточно активно некоторое время.

Пример: допустим, вы решили провести крупномасштабный рефактроинг вашей программной системы.

Основные действия, которые нужно будет делать — внесение различных полуавтоматизированных изменений в код (переименования символов, извлечения кусков кода в отдельные функции, изменения сигнатур функций, и.т.д).
Кроме того, вам гораздо чаще придётся прыгать с места на место по проекту, смотреть на различные диаграммы классов, иерархии вызовов, и.т.д.

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

Для борьбы с этой проблемой некоторые программисты делают себе разнообразные cheat-sheets со списками команд, которые полезны в редких случаях. Когда такой случай наступает, приходится обращаться к шпаргалке (или гуглить).

Вторая проблема — сложность миграции между системами.

Если вам по какой-либо причине пришлось сменить IDE, заученные на уровне мышечной памяти горячие клавиши старой среды разработки могут оказаться серьёзным барьером.
Придётся переучиваться, а это не очень приятное и непродуктивное занятие.

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

Третья проблема — совместное использование нескольких систем.

Тут всё может быть совсем плохо. Одни и те же действия в разных системах могут иметь разные комбинации горячих клавиш.
В этом случае приходится постоянно держать в голове по набору комбинаций для каждой системы.
Кроме того, нужно всё время мысленно контролировать, что за система используется прямо прямо сейчас.
Ключевое тут: «держать в голове», — о мышечной памяти можно забыть, что повлечёт за собой падение производительности.

Допустим, вы отлаживаете клиент-серверную систему, где сервер написан на с++ в Visual Studio, а клиент — приложение на java в IDEA или eclipse.

Сервер формирует пакет с данными, отправляет их клиенту, который их каким-то образом обрабатывает.
Сначала нам необходимо разобраться, правильно ли серверная часть генерирует пакеты для отправки.
Затем, после отправки данных сервером, нужно будет в дебаггере на клиентской стороне смотреть, как полученные данные обрабатываются.
Причём, общение сервера с клиентом может не ограничиться пересылом одного пакета.
В этом случае вам придётся перескакивать туда-обратно между дебаггерами не один раз.
Основные действия, которые будут делаться вами в данном случае — команды пошагового исполнения программы (step over, step into, step out).
Концептуально, эти действия одни и те же, однако в каждой из сред разработки за них отвечают свои функциональные клавиши:

VS eclipse IDEA
step into F11 F5 F7
step over F10 F6 F8
step out Shift+F11 F7 Shift+F8

При этом, например, ‘step into’ в eclipse делается клавишей F5, которая в VS отвечает за ‘resume execution’, что может сбить весь процесс отладки по неосторожности.

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

Цель

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

Требования к системе:

  • Отсутствие необходимости пользователю помнить фактические значения комбинаций горячих клавиш.
  • Простота отправки команд пользователя приложению. Интерфейс должен позволять организовывать команды таким образом, чтобы их можно было легко и быстро находить и выполнять.
  • Простота настройки системы — пользователь должен иметь возможность подстраивать интерфейс под свои нужды, ограничивать только актуальными командами в данный конкретный момент.
  • Возможность легко переиспользовать конфигурации команд между схожими приложениями.
  • Возможность автоматически контролировать, что окно-адресат для команды активно в момент исполнения команды (чтобы избежать ситуаций, таких как проблема описанная выше — клавиша F5 в дебаггерах eclipse и visual studio).
  • Кросплатформенность приложения — хотелось абстрагироваться не только от фактических комбинаций горячих клавиш, а также и от операционной системы.

Основная идея системы — на компьютере запускается сервер, который отвечает за генерацию клавиатурных команд. Он работает в бэкграунде и отправляет команды активному в тот момент окну. К этому серверу подключается android устройство, которое используется для пользовательского ввода/вывода. Все команды инициируются нажатиями на кнопки, отображаемые на android устройстве. Когда это происходит, сервер отправляет соответствующую комбинацию клавиш активному окну на своей стороне.

Поскольку сопоставление соответствующей комбинации клавиш для данного действия происходит автоматически на сервере, можно будет определить несколько правил такого сопоставления. Это позволяет единообразно работать с разными программами (если наборы действий, производимых в них, схожи).

Конфигурирование сервера

Для работы данной системе необходимо иметь следующую информацию:

  • Данные об окружениях, в которых мы работаем, и для которых мы хотим эмулировать горячие клавиши.
  • Данные о комбинациях горячих клавиш. Необходимо для каждого действия указать комбинацию горячих клавиш, которая его инициирует в каждом из окружений.
  • Конфигурация элементов на клиентском устройстве: расположение, вид и поведение кнопок, которые будут использоваться для отправки команд.

Данные о горячих клавишах в разных окружениях

Данные из первого и второго пункта хранятся в простой таблице (текстовый csv файл с табуляциями в качестве разделителей), которая выглядит примерно так:

Пример конфигурации команд

В нём каждая строка представляют всю информацию об определённой команде. Первые четыре столбца — служебные. В них хранятся данные, которые используются программой для построения интерфейса пользователя:

  1. command_id — уникальное строковое представление команды в системе
  2. command_category — строковое представление категории, к которой команда относится
  3. command_note — короткое описание команды, которое будет написано на её кнопке в пользовательском интерфейсе
  4. command_description — более детальное описание команды

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

Построение интерфейса пользователя

Для писания пользовательского интерфейса используется второй текстовый конфигурационный файл. Интерфейс состоит из набора страниц. На страницах расположены кнопки, каждая из которых соответствует какой-либо команде.

0. простейший вариант

Вот пример простейшей конфигурации страницы и то, как она будет выглядеть на клиентском устройстве:

содержимое конфигурационного файла

page:main page
debug_start
debug_stepInto
debug_stepOver
debug_stepOut
debug_restart

В конфигурации интерфейса просто прописаны идентификаторы команд — по одному в строчку. Это транслируется в такой интерфейс для пользователя:

представление на клиентском устройстве

представление на клиентском устройстве
Если какое-то из действий не определено для данного окружения, то соответствующая кнопка становится неактивна.

1. Несколько кнопок в ряд, промежутки между кнопками

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

Следующая конфигурация иллюстрирует данные вещи:

Вертикальные и горизонтальные промежутки между кнопками

page:main page
debug_start;debug_restart;;
;
;debug_stepOver;;
debug_stepInto;;debug_stepOut
;

Интерфейс, в который это транслируется для пользователя:

представление на клиентском устройстве

представление на клиентском устройстве

2. Переиспользование кнопок

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

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

Конфигурация с опциями

page:main
debug_start;debug_restart,debug_stopDebugger;;
;
;debug_stepOver;;
debug_stepInto;;debug_stepOut
;

Вид интерфейса в различных окружениях:

представления различных окружений

представления различных окружений

Обратите внимание, как кнопка ‘restart’ в окружении для VS превращается в ‘stop debug’ в окружении для eclipse, поскольку для eclipse не определена комбинация для ‘restart debug’.

3. Опции.

Многие команды по факту являются целыми группами команд, которые по смыслу схожи, поэтому хочется их объединить вместе в клиентском UI, однако не хочется, чтобы они занимали слишком много места.
Для примера можно привести команды типа ‘build something’: ‘build solution’, ‘build project’, ‘compile file’.
Для таких групп команд можно определить специальную страницу, которая будет автоматически появляться и исчезать при нажатии кнопки группы.
Эта специальная страница отличается от стандартной только заголовком — тело у неё такое же.

Пример такой конфигурации

page:main page
debug_start;debug_restart;;
;
do_step
;
optionsSelectorPage:do_step;steps
;debug_stepOver;;
debug_stepInto;;debug_stepOut

Пример работы в UI:

Работа с селектором опций

Работа с селектором опций

Этот подход очень удобен с командами типа ‘[сделать определённое действие] с [чем-то]’, где выбирается ровно один вариант из нескольких для [чего-то].
Все варианты областей действия команды переносятся на дополнительную страницу и автоматически отображаются, когда нужно.

Страницы опций можно также вкладывать одна в другую, образуя более глубокую и разветвлённую структуру.

Привязка к определённому окну ввода

Для того, чтобы не следить за тем, какое окно сейчас активно (чтобы симулируемый клавиатурный ввод не отправился кому попало), можно попросить программу контролировать это.
Для этого системе при запуске передаётся опция ‘stickEnvToWindow’.
В этом случае при выборе нового окружения, пользователю будет отображаться такой запрос:

Запрос активного окна

Запрос активного окна

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

Активное окно не в фокусе

Активное окно потеряло фокус

В этом случае пользователь должен либо вернуть фокус нужному окну и снова запросить выполнение команды, либо отменить команду:

Демонстрация отслеживания окна ввода

Отслеживание окна ввода

Автоматизация клавиатурного ввода

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

Для себя я регулярно использую подобные вещи:

Вставка слов или фраз на другом языке

Часто при наборе русского текста мне приходится переключаться в английскую раскладку ради одного слова, или даже пары символов.
Русская раскладка не признаёт существования следующих символов: ‘ < > [ ] { } $ # @ & ^ ~, некоторые из которых бывают нужны регулярно.
Кроме того, в русском тексте могут встречаться повторяющиеся термины на английском, а, если это html, то ещё и тэги.

Симуляцию набора текста в другой раскладке можно реализовать просто добавив команды переключения раскладки клавиатуры перед и после фактического набора вставляемого текста.

Например, чтобы напечатать тэг <html>, нужно использовать следующую комбинацию: ‘+{COMMA}html+{PERIOD}’. Она правильно сработает, если включена английская раскладка.
Если же включена русская раскладка, то нужно добавить в начало и конец комбинации горячую клавишу переключения раскладки.
В моём случае, это ‘%{SHIFT}’ (Alt+Shift).
Таким образом, если в текстовом редакторе включена русская раскладка клавиатуры, то следующая последовательность символов: ‘%{SHIFT}+{COMMA}html+{PERIOD}%{SHIFT}’ напечатает там <html>.

Пример работы:

Ввод текста в двух раскладках

Ввод текста в двух раскладках

In-place преобразование выделенного слова

Допустим, мне нужно в большом количестве мест в коде обернуть некоторые переменные в макрос или вызов функции. Например, у какой-то из функций нужно изменить тип параметра, поэтому во всех местах, где эта функция вызывается, нужно сконвертировать то, что туда передавалось в новый тип.

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

Шаги, которые нужно сделать в данном случае:

  1. выделяем переменную двойным щелчком мыши
  2. симулируем команду «вырезать» (Ctrl+x)
  3. симулируем набор текста — название оборачивающей функции и отрывающейся скобки
  4. симулируем команду «вставить» (Ctrl+v)
  5. закрываем скобку

Шаги 2-5 автоматизируются следующей комбинацией: ‘^{X}my+function+name+9^{V}+0’.
Пример работы:

Оборачивание выделенного текста

Оборачивание выделенного текста

Кроме того, возможно, в разных местах кода вид этих вызовов может немного различаться (возможно, где-то нужно указывать пространство имён вызываемой функции, передавать дополнительные параметры, и.т.д). Всё эти вариации легко реализовать и иметь под рукой.

Заключение

Система пока что сырая — это скорее прототип, а не конечная версия.
Но, как мне кажется, уже в её нынешнем состоянии, она способна упростить и повысить эффективность работы с горячими клавишами.

Технические детали реализации я в данной статье не затрагивал, чтобы не раздувать её. Если будет интерес — можно будет сделать статью, посвящённую этому.

Код реализации лежит у меня на гитхабе. Там же есть немного более формальной документации о форматах конфигурационных файлов и аргументов командной строки.

Если не хочется качать исходники и компилировать код, есть собранная для windows версия (для запуска нужно будет поставить
Microsoft Visual C++ 2015 Redistributable).
Для самостоятельной компиляции исходников нужен C++14 — совместимый компилятор и boost (используются boost::asio и boost::program_options). В документации есть описание того, как собирать проект при помощи gcc и Visual Studio.

Буду рад услышать критику по поводу реализации, а также предложения о том, что можно добавить в систему. Кроме того, сейчас мне не очень нравится формат конфигурационного файла описания UI. Если у кого-нибудь есть идеи, как можно лучше представить подобный интерфейс (предпочтительно в простом текстовом формате), прошу в комментарии.

Что ещё хочется добавить в данной системе:

  • Возможность выполнять несколько команд одним запросом (это серьёзно улучшит функциональность по симуляции набора текста).
  • Автоматическое отслеживание и переключение раскладки клавиатуры активного окна.
  • Улучшение функциональности работы с конфигурационными файлами, добавление новых типов конфигов.
  • Рефакторинг кода программы.
  • Улучшение системы сообщений пользователю о проблемах в конфигурационных файлах (сейчас сообщения не всегда информативны).
  • Багфиксы симуляции некоторых комбинаций клавиш (см. ниже).

Все ссылки в одном месте:

P.S.

Проблемы и неочевидности, о которых стоит знать перед использованием:

  1. Поддерживается только кодировка UTF-8. Если программе скормить конфиг в другой кодировке, она может выругаться довольно неочевидным сообщением. Пожалуйста, учитывайте это, и сохраняйте файлы в правильной кодировке.
  2. В windows замечена проблема с симуляцией нескольких комбинаций формата Shift+’smth’. Она проявляется для клавиш PgUp, PgDn, Home, End, Insert, Delete и стрелочек. Комбинация типа ‘+{INSERT}’, которая должна симулировать Shift+Insert, по факту симулирует просто нажатие Insert.
  3. В примерах конфигураций многие комбинации горячих клавиш не проверялись на работоспособность. Я просто нагуглил несколько списков и объединил их в общий файл. Поэтому, там возможны ошибки. Буду признателен сообщениям о найденных проблемах в конфигах, а также pull-реквестам.

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


Комментарии

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

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