Последние три недели я работал над рефакторингом и исправлением легаси-кода (самые старые части которого были написаны в 2013 году), отвечающего за позиционирование иконок в KDE Plasma, а также сохранение и загрузку этих данных.
Вот мой запрос на внесение изменений: plasma-desktop: Refactor icon positioner saving and loading.
Имевшийся код работал, но происходили всякие странности типа состояний гонки, когда иконки вдруг располагались в необычном порядке, плюс частично перемешался код фронтенда и бэкенда.
И я никого в этом не виню. Код по своей природе склонен с течением времени искажаться, особенно в опенсорсных проектах, где его может изменять множество разных людей.
Бывало у вас такое, что провода наушников спутываются, когда вы убираете их в шкаф или кладёте ненадолго в карман? И с базами кода происходит нечто похожее, когда множество людей записывают в них изменения, исправляя баги друг друга. Каждый мыслит по-своему, поэтому вполне естественно, что всё со временем запутывается.
В итоге периодически кому-то нужно разгребать такую запутанную базу кода и подчищать её.
▍ Самое сложное — это чтение кода
Читая старый код, особенно такой, где комментарии делались редко, бывает очень непросто понять, что в нём происходит. Честно говоря, бо́льшую часть времени я пытался разобраться, как он вообще работает, что и когда вызывается, где обновляется расположение иконок и так далее.
Когда я, наконец, более-менее разобрался в происходящем, то смог начать наводить порядок. Я присвоил множеству методов более описательные имена и переместил часть кода — в частности, отвечающий за сохранение позиций иконок — из фронтенда обратно в бэкенд.
▍ Экраны и иконки
Каждый экран (монитор ПК, ТВ и так далее) имеет свои особенности. Некоторые, будучи подключёнными через DisplayPort, при переходе ПК в режим энергосбережения отключаются. Другие подключение сохраняют, но показывают чёрный экран.
Одна из серьёзных проблем с отображением иконок была в том, что при отключении экрана код считал, что экрана больше нет, и удалял иконки с рабочего стола.
Вполне разумно. Зачем отображать иконки на экране, которого нет? Но когда ваш монитор при переходе ПК в режим сна вслед за ним отключается, начинают происходить неправильные вещи.
Сейчас в основе обработки этого состояния лежит проверка, используется ли экран. Теперь, когда экран не используется, мы просто ничего с иконками не делаем. Можно вообще их не трогать.
▍ Полосы и разрешение экрана
В нашем алгоритме позиционирования иконок используются некие «stripe» (полосы).
У каждого разрешения есть своё количество таких полос. В свою очередь, полосы содержат массив иконок или пустых участков.
Поэтому, если разрешение вашего экрана, скажем, 1920х1080
, то мы вычисляем, сколько полос он содержит, и сколько элементов на каждую полосу вместится.
Stripe1: 1 2 3 4 5 6 7
Stripe2: 1 2 3 4 5 6 7
Stripe3: 1 2 3 4 5 6 7
И так далее..
Но когда вы изменяете разрешение экрана или масштаб, это влияет на то, сколько полос у вас получится, и сколько иконок уместится в каждую.
Поэтому, для тех экранов, которые при переходе системы в режим сна отключаются, раньше число полос менялось на 1 строку, 1 столбец. В итоге алгоритм размещения иконок паниковал и показывал их все в этой крохотной области 1,1.
После этого, когда экран снова включался, алгоритм недоумевал, что происходит, и восстанавливал подобающее число и размер полос. Вот только к тому моменту все прежние данные позиционирования уже были потеряны, и расположение иконок обновлялось. У пользователя в этот момент тоже возникало недоумение, почему иконки вдруг переместились.
В этом случае нам снова нужно проверять, используется ли экран. Но есть и другие проблемы.
▍ Сохранение позиций иконок
Прежний код сохранял позиции иконок при каждом их изменении. Разумно.
Но в нём не учитывался сценарий с выключением экрана… и в итоге позиции иконок сохранялись, пока рабочий стол находился в своём неестественном состоянии. Это тоже может раздражать, поскольку некоторые люди располагают иконки по-своему, а после очередного выкрутаса экрана они снова оказываются не в тех местах.
В нашем случае позиции иконок обновлялись после каждого вызова отрисовки, если изменялись. И это подразумевало довольно частое сохранение независимо от того, что вызвало их перемещение.
Пришлось разделить действия пользователя и системы. Если иконки сохраняет компьютер, в идеале нам не нужно сохранять их позиции, если только не случится нечто радикальное вроде изменения разрешения.
Позиции иконок сохраняются в соответствии с разрешением, поэтому если вы меняете их расположение на экране 3440х1440
, а затем переключаете его разрешение на 1920х1080
, то их позиции тоже изменятся. Ранее эта часть кодовой базы не работала, и постоянно перезаписывалась старая конфигурация, создавая головную боль.
Поэтому теперь мы сохраняем позиции иконок, только когда пользователь:
- добавляет иконку или удаляет,
- перемещает иконку,
- изменяет разрешение экрана.
Это значительно снижает непредсказуемость расположения иконок, так как сохранение их позиций происходит только после явных действий пользователя.
▍ Ошибки отступов
Последнее, что создавало проблемы с позиционированием иконок — это то, что доступная для их отображения область рабочего стола определялась до загрузки панелей. Появление панелей на экране сокращало доступную под иконки площадь, причём в процессе их загрузки эта площадь изменялась неоднократно, пока не загружались полностью все панели.
В прежнем коде это вызывало смещение иконок с обновлением их позиций и последующим сохранением этих позиций.
Предположим, вы расположили иконки нужным вам образом, но при следующей загрузке Plasma панели начинают их сдвигать…и в итоге они оказываются не на своих местах.
Эта проблема уже была частично исправлена исключением сохранения позиций иконок при их перемещении системой: мы просто загружаем позиции иконок, когда экран используется, на чём их позиционирование на рабочем столе заканчивается. Часть изменений отступов происходит при отключённом экране.
Но нам ещё нужно исправить этап загрузки. В идеале область с иконками должна загружаться последней, чтобы они получали соответствующие отступы, на которые появление панелей уже не повлияет. Но в мой текущий запрос на внесение изменений эта доработка не входила.
▍ Заключение
С виду всё это может показаться не столь существенным, но по факту потребовалось много работы. Я несколько дней обдумывал эту проблему, пытаясь понять, что происходит, и как это улучшить.
К счастью, благодаря активной помощи ревьюеров и тестировщиков, я смог значительно оптимизировать работу этого механизма. Когда я берусь решать какую-то проблему, то кропотливо вникаю во все детали, поэтому очень ценю терпение, которое они проявили ко мне и моим вопросам :D
Чего мне больше всего в этом проекте не хватало, так это комментариев в коде. Вам не нужно комментировать очевидные вещи, но всему остальному пояснения не помешают. Конечно, порой трудно оценить, что очевидно, а что нет, но здесь есть простой ориентир: «Если вы не знаете, очевидно что-то или нет, то наверняка нет, поэтому лучше добавьте комментарий».
Надеюсь, что после всех этих изменений иконки рабочего стола будут работать стабильнее. Если же вы заметите какие-то баги, пишите на https://bugs.kde.org.
Спасибо за чтение! :)
PS. Самое забавное в этой истории, что лично я предпочитаю, когда на рабочем столе вообще нет иконок :'D
Автор: Bright_Translate