[Перевод] Как я неделю просидел над десятью строками кода

masd72zxdvoj7zd7ktb_xgrnvq8.png

Недавно я столкнулся с серьезным препятствием, когда работал над возможностью перетаскивать вкладки в приложении Warp: если попытаться передвинуть конкретную вкладку, она потянет за собой всё окно. Понадобилась целая неделя изысканий и экспериментов, чтобы установить, откуда берет начало этот баг. Но в конечном итоге я исправил это в pull request-е, который состоял менее чем из десяти строк кода! Это несоответствие усилий выхлопу также заставило меня проникнуться сознанием того, что создание ПО включает в себя гораздо больше, чем просто написание кода. В этой статье я расскажу, как всё происходило.

Немного контекста: Warp — это терминал для разработчиков на базе Rust. При его создании мы использовали собственный кастомный UI-фреймворк, так что всё, что касается вкладок и перетаскивания, пришлось разрабатывать с нуля.
На первый взгляд проблема, которой я занимался, выглядела несложной: как дать пользователям возможность перемещать вкладки так, чтобы не двигалось всё окно? Такое поведение можно встретить в других приложениях — Chrome или Electron, если выставить нужные настройки, так что я знал, что сделать это реально. Однако каждый раз, когда я пытался дублировать это поведение в Warp или простеньких тестовых приложениях, окно с панелью заголовка упорно тащилось следом. Не имело значения ни то, что панель не содержала контента и была скрыта, ни то, что я что-то изобразил на этом участке.

xnf62i6nkfjzzwy-iuf1izwwzte.gif

В попытках разобраться в проблеме я обратился сперва к документации Apple для API к macOS. К сожалению, на деталях эта документация особо не останавливается, и я не сумел отыскать какого-то одного четкого значения, которое нужно было бы задать, чтобы предотвратить перетаскивание. Там было очень подробно расписано, как инициировать перемещение через другие части окна, но не указывалось ничего для отключения перетаскивания панели. То немногое, что удалось найти, я опробовал на тестовом приложении, результата это не дало.

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

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

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

Наконец, спустя несколько дней, которые не принесли никакого существенного прогресса в поиске решения, я наткнулся на комментарий в кодовой базе Chromium, где говорилось о возможной оптимизации перемещающего поведения. Этот комментарий приводил ссылку на запись в системе отслеживания ошибок. Сама запись не давала дополнительного контекста, но, в свою очередь, ссылалась на сообщение в коммите, и вот там-то меня и ждало озарение, в котором я нуждался:

jizybo8f_9uhhgv_q4yp2_oaisu.png

[MacViews] Исправлено непреднамеренное перемещение окон, особенно при попытке переместить вкладки.
Есть два пути, которые приводят к перетаскиванию окна приложения на Mac по экрану:
— Медленный путь инициируется приложением: происходит проверка попадания при нажатии на кнопку мыши, и что-то (чаще всего представление фрейма) вызывает »[self.windowperformWindowDragWithEvent: theEvent]», чтобы начать перетаскивание на стороне сервера.
— Быстрый путь предполагает, что окно пересылает на сервер карту с зонами, для которых доступно перетаскивание. При «mouseDown» в этих зонах перетаскивание начинается, не дожидаясь приложения.


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

Еще одна сложность: представление, которое располагается за панелью заголовка окна при полноразмерном представлении контента, даже если панель скрыта, создает пробел в карте перетаскивания, только если аннулирует «mouseDown» и возвращает YES для `acceptsFirstResponder`.


Наконец-то у меня появилось разумное предложение, для которого была прописана ясная логика, и оно помогло мне отчетливее понять проблему, которая передо мной стояла. Я включил необходимые настройки в тестовое приложение, и оно тут же заработало именно так, как я и ожидал! В результате портирования этих изменений непосредственно в Warp образовался крохотный pull request с едва ли не самым высоким соотношением усилий к выхлопу в моей карьере:

t-ibc-f2hvyqyvnhg7cavady1sm.png

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

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

vwgo4zmcgfcf90do05mpygq6z0k.gif

Если вам интересно собственноручно опробовать перетаскивание или взглянуть на терминал для разработчиков на базе Rust — скачивайте Warp вот здесь.

© Habrahabr.ru