Последние три недели я работал над рефакторингом и исправлением легаси-кода (самые старые части которого были написаны в 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)


  1. QtRoS
    17.11.2024 09:58

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


  1. Mingun
    17.11.2024 09:58

    Интересно, автор пеняет на отсутствие комментариев, но сам почему-то не добавил комментарии для вновь добавленных полей и методов. Например, что такое screenUsed()? Что включается в это понятие? Когда оно может поменяться? Или что такое m_applet, зачем он нужен?
    (со стилями хабра не видно, но код является ссылками в PR. @Boomburum, можно как-то подправить стили, чтобы ссылки выделялись?)

    Чувствую, следующему исследователю багов придется вновь проделывать ту же работу по исследованию.


    1. Boomburum
      17.11.2024 09:58

      Можете в личку побольше подробностей прислать? ) Сходу не увидел аномалий, но может не то и не там смотрю.


      1. Mingun
        17.11.2024 09:58

        Вот код без ссылки: screenUsed()
        А вот со ссылкой: screenUsed()

        Найдите 10 отличий :)


        1. Survtur
          17.11.2024 09:58

          На всякий случай уточню, что у меня различий не видно. Вдруг у вас видно...


          1. Wesha
            17.11.2024 09:58

            А Вы мышкой шуровать пробовали?


            1. Survtur
              17.11.2024 09:58

              Не не, я понял, что в одном месте ссылка, а в другом нет. Но без шурования-то этого не видно. Мало ли, вдруг это баг...


              1. Mingun
                17.11.2024 09:58

                Конечно баг. Я о том и говорю, что без наведения мышки непонятно, есть ссылка на элементе с кодом или нет


    1. Hlad
      17.11.2024 09:58

      "при правильном названии переменных комментарии не нужны" (с)


  1. gimcnuk
    17.11.2024 09:58

    Зачем нужен рабочий стол, если на нём нет иконок?


    1. Mingun
      17.11.2024 09:58

      Ради красивой картинки, очевидно же!


      1. Andy_U
        17.11.2024 09:58

        Один я вспомнил скриншот экрана, очевидно, какой-то молодой дурочки, которая случайно сделала некую неприличную картинку обоями рабочего стола, сама исправить не сумела, к админу обратиться постеснялась, и аккуратно "залепила" все "достоинство" стриптизера иконками разных документов?


        1. klounader
          17.11.2024 09:58

          вспомнилось https://www.youtube.com/watch?v=yM9ugemg5Rw


      1. Wesha
        17.11.2024 09:58

        Ради красивой картинки

        Как-то Сисадмин спросил:
        — Учитель, не желаете ли красивую картинку для вашего десктопа? У меня есть хорошая коллекция обоев для рабочего стола со звездным небом и моральным законом.
        — Почему ты думаешь, что мой нынешний wallpaper хуже? — спросил в ответ Инь Фу Во.
        — Я не знаю, какая у вас картинка сейчас. Я никогда не видел вашего десктопа. У вас всегда открыто множество окон.
        — Я тоже его никогда не видел, — сказал Инь. — Я работаю.


        1. Mingun
          17.11.2024 09:58

          Открою вам секрет: у меня тоже практически никогда не виден рабочий стол. Но картинка красивая там стоит. И так приятно на неё взглянуть, когда случайно мышка в правый нижний угол залетает. Ну или когда ловишь мельком загрузку рабочего стола в начале рабочего дня.


          1. Wesha
            17.11.2024 09:58

            Я в конце рабочего дня Hibernate делаю.


  1. kenomimi
    17.11.2024 09:58

    Автор реально герой, там сложность уровня большой корпоративной системы.

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


    1. indestructable
      17.11.2024 09:58

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


  1. AVX
    17.11.2024 09:58

    О, какой больной вопрос! И ведь тянется это с хз каких времён. Вечная проблема была - запустил игру - не тянет в родном разрешении - поменял (в игре) на меньшее - оп, после выхода разрешение восстановилось как было, но иконки все скукожились по размеру меньшего разрешения. Сейчас в основном это пофиксили, и после выхода из игры и восстановлении разрешения иконки на своих местах. Однако, если например игра повисла или по какой-то причине её прибить принудительно - разрешение экрана остаётся как было задано в игре, иконки естественно переместились, и когда возвращаешь вручную исходное - так и остаются сдвинутыми.

    Мне было бы например удобно, чтобы в контекстном меню рабочего стола было что-то типа "запомнить расположение значков" и "восстановить расположение значков", или переработать пункт ПКМ - значки - зафиксировать значки. Чтобы даже изменение разрешения не сдвигало значки (правда, как их потом достать, если например монитор вдруг поставили поменьше и другого нет, и разрешение больше не поставить уже). В общем, тут много нужно продумывать вариантов. Непростая задача.

    Хорошее и нужное дело, удачи в этом!


    1. Mingun
      17.11.2024 09:58

      правда, как их потом достать, если например монитор вдруг поставили поменьше и другого нет, и разрешение больше не поставить уже

      Таким же пунктом в меню "перераспределить значки"


      1. redfox0
        17.11.2024 09:58

        С использованием сортировки пузырьком! https://habr.com/ru/companies/vdsina/articles/544218/


    1. me21
      17.11.2024 09:58

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


      1. NN1
        17.11.2024 09:58

        А попробуйте внешний установить главным экраном. Тогда с главного на главный будет переносить.