Последние три недели я работал над рефакторингом и исправлением легаси-кода (самые старые части которого были написаны в 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
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
Комментарии (23)
Mingun
17.11.2024 09:58Интересно, автор пеняет на отсутствие комментариев, но сам почему-то не добавил комментарии для вновь добавленных полей и методов. Например, что такое
screenUsed()
? Что включается в это понятие? Когда оно может поменяться? Или что такоеm_applet
, зачем он нужен?
(со стилями хабра не видно, но код является ссылками в PR. @Boomburum, можно как-то подправить стили, чтобы ссылки выделялись?)Чувствую, следующему исследователю багов придется вновь проделывать ту же работу по исследованию.
Boomburum
17.11.2024 09:58Можете в личку побольше подробностей прислать? ) Сходу не увидел аномалий, но может не то и не там смотрю.
Mingun
17.11.2024 09:58Вот код без ссылки:
screenUsed()
А вот со ссылкой:screenUsed()
Найдите 10 отличий :)
gimcnuk
17.11.2024 09:58Зачем нужен рабочий стол, если на нём нет иконок?
Mingun
17.11.2024 09:58Ради красивой картинки, очевидно же!
Andy_U
17.11.2024 09:58Один я вспомнил скриншот экрана, очевидно, какой-то молодой дурочки, которая случайно сделала некую неприличную картинку обоями рабочего стола, сама исправить не сумела, к админу обратиться постеснялась, и аккуратно "залепила" все "достоинство" стриптизера иконками разных документов?
Wesha
17.11.2024 09:58Ради красивой картинки
Как-то Сисадмин спросил:
— Учитель, не желаете ли красивую картинку для вашего десктопа? У меня есть хорошая коллекция обоев для рабочего стола со звездным небом и моральным законом.
— Почему ты думаешь, что мой нынешний wallpaper хуже? — спросил в ответ Инь Фу Во.
— Я не знаю, какая у вас картинка сейчас. Я никогда не видел вашего десктопа. У вас всегда открыто множество окон.
— Я тоже его никогда не видел, — сказал Инь. — Я работаю.Mingun
17.11.2024 09:58Открою вам секрет: у меня тоже практически никогда не виден рабочий стол. Но картинка красивая там стоит. И так приятно на неё взглянуть, когда случайно мышка в правый нижний угол залетает. Ну или когда ловишь мельком загрузку рабочего стола в начале рабочего дня.
kenomimi
17.11.2024 09:58Автор реально герой, там сложность уровня большой корпоративной системы.
А почему бы не сделать так... Пользователь говорит, например, хочу сетку 10х15, как в лончерах в андроиде. Иконки и виджеты масштабируются по этой сетке в относительных величинах. Какой бы не был экран, расположение иконок и виджетов останется прежним. Для вертикальной и горизонтальной ориентации свои раскладки, скукоживать и разуплотнять не надо. Раскладка для каждого монитора хранится отдельно и бесконечно.
indestructable
17.11.2024 09:58Или хранить положение иконок и виджетов в процентах ширины от краев экрана (причем от ближнего (левого, верхнего) или дальнего (правого, нижнего) края, в зависимости от того, к какому краю иконка ближе. И при изменении разрешения проецировать позиции иконок на сетку.
AVX
17.11.2024 09:58О, какой больной вопрос! И ведь тянется это с хз каких времён. Вечная проблема была - запустил игру - не тянет в родном разрешении - поменял (в игре) на меньшее - оп, после выхода разрешение восстановилось как было, но иконки все скукожились по размеру меньшего разрешения. Сейчас в основном это пофиксили, и после выхода из игры и восстановлении разрешения иконки на своих местах. Однако, если например игра повисла или по какой-то причине её прибить принудительно - разрешение экрана остаётся как было задано в игре, иконки естественно переместились, и когда возвращаешь вручную исходное - так и остаются сдвинутыми.
Мне было бы например удобно, чтобы в контекстном меню рабочего стола было что-то типа "запомнить расположение значков" и "восстановить расположение значков", или переработать пункт ПКМ - значки - зафиксировать значки. Чтобы даже изменение разрешения не сдвигало значки (правда, как их потом достать, если например монитор вдруг поставили поменьше и другого нет, и разрешение больше не поставить уже). В общем, тут много нужно продумывать вариантов. Непростая задача.
Хорошее и нужное дело, удачи в этом!
Mingun
17.11.2024 09:58правда, как их потом достать, если например монитор вдруг поставили поменьше и другого нет, и разрешение больше не поставить уже
Таким же пунктом в меню "перераспределить значки"
redfox0
17.11.2024 09:58С использованием сортировки пузырьком! https://habr.com/ru/companies/vdsina/articles/544218/
me21
17.11.2024 09:58Теперь новая беда, не с иконками, а с окнами. Windows теперь умеет определять, когда внешний монитор пропал, и автоматически переносит окна на встроенный в ноутбук. Но при новом подключении внешнего монитора расположение окон не восстанавливает. Вот это прям беда.
NN1
17.11.2024 09:58А попробуйте внешний установить главным экраном. Тогда с главного на главный будет переносить.
QtRoS
Просто не надо было изначально делать логику такого характера на QML, особенно с использованием таймеров.