Проблемы, с которыми мы столкнулись при обновлении интерфейса PVS-Studio

в 7:51, , рубрики: .net, C#, GUI, icons, interface, pvs-studio, Visual Studio, Блог компании PVS-Studio, разработка под windows

Picture 2 В недавно вышедшей новой версии PVS-Studio 6.10 был существенно обновлён графический пользовательский интерфейс Visual Studio плагинов и Standalone версии. Предыдущая версия интерфейса, несмотря на постоянную эволюцию (добавлялись и исчезали новые кнопки и пункты меню), просуществовала практически 6 лет без существенных изменений, впервые появившись в PVS-Studio версии 4.0 в 2010 году.

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

Проблемы старого интерфейса PVS-Studio, и почему мы решили его поменять

Для начала немного истории. Отдельное окно PVS-Studio появилось в Visual Studio плагине начиная с 4-ой версии анализатора. Предыдущие версии использовали для вывода диагностических сообщений стандартное окно Error List, однако, наши требования быстро переросли возможности этого окна. Помимо невозможности добавлять свои фильтры, контекстные меню, кнопки и т.п., главной причиной, побудившей нас реализовать собственное окно для сообщений PVS-Studio, стала производительность Error List'а. При записи в него хотя бы нескольких тысяч сообщений, работать с ним становилось невозможно — студия подвисала даже просто при попытке осуществить его прокрутку.

Конечно, можно сказать: анализатор, выдающий тысячи сообщений, это плохой анализатор. Однако, следует учитывать, что нашими первыми диагностиками были ошибки портирования C++ программ на 64-битную платформу, и, по своей природе, такие ошибки могут быть весьма многочисленными, особенно на реальных крупных проектах. По секрету, могу сказать, что нашим «анти-рекордом» был отчёт на ~250 000 сообщений! Тут же хочу успокоить наших пользователей — современная версия PVS-Studio такого себе обычно не позволяет, тем более сейчас у нас появились такие концепции, как «уровни важности» ошибок и различные способы легко массово подавлять или отключать неудачные срабатывания (которые неизбежно встречаются в рамках методологии статического анализа). Однако, в далёком 2010 году всего этого ещё не было, и «тормоза» интерфейса были как никогда актуальны.

Первые версии нашего плагина с отдельным окном поддерживали Visual Studio версий 2005 и 2008. Нужно сказать, что современная Visual Studio также не стоит на месте, и проблема с отображением большого числа сообщений в стандартном Error List'е у неё уже возможно и не так актуальна, но преимущества отдельного «окна вывода» всё равно очевидны. Забавно здесь провести параллель со встроенным C++ анализатором Visual Studio — в определённый период (кажется, на версиях 2010 — 2012, но я могу ошибаться), этот анализатор также для отображения результатов своей работы использовал «собственное», отдельное окно вывода. Однако, в последних версиях Visual Studio вернулась к отображению результатов в стандартном окне Error List. О причинах, по которым, на наш взгляд, разработчики Visual Studio это сделали, я расскажу, когда буду описывать наше новое окно.

Как я уже упомянул выше, первые версии окна PVS-Studio делались для 2005 и 2008 версий среды Visual Studio. Таким образом, при разработке изначального дизайна нашего окна, мы ориентировались на дизайн стандартного Error List'а именно из этих версий IDE. Этим в основном и объясняется тот стиль иконок, который мы взяли тогда на вооружение. Несмотря на то, что в современных версиях Visual Studio наше окно поддерживало все новомодные цветовые темы оформления, иконки в нём остались ещё с тех времён. Вот так выглядели основные компоненты нашего интерфейса до «рестайлинга»:

Picture 11

Рисунок 1 — старый интерфейс PVS-Studio в Visual Studio 2015 (окно вывода и главное меню)

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

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

Новый дизайн основного окна

Перед тем, как начинать писать код, мы обратились к профессиональному дизайнеру с задачей создания концепции нового интерфейса. Как я упоминал ранее, наше окно повторяло концепцию стандартного Error List'а, и, соответственно, центральной его частью был классический «грид» — таблица со столбцами и строками. Мы хотели поэкспериментировать и попробовать отказаться от «классической» формулы, тем более сейчас в моде минималистичность, и сама Visual Studio подвержена данным тенденциям в своём интерфейсе. В качестве нового «вдохновения» мы решили попробовать использовать опять элементы Visual Studio — окно нотификаций и окно вывода результатов встроенного C++ анализатора.

Picture 3

Рисунок 2 — окно встроенного статического анализа (Visual Studio 2012) и окно нотификаций (Visual Studio 2013)

Как видно из картинки, оба окна максимально минималистичны — не содержат ничего, кроме текста сообщения и какого-нибудь идентификатора (код или цветовой маркер). На левом окне есть также поля для поиска/фильтрации. Также, в отличии от Error List, оба окна ориентированы скорее на вертикальное, а не горизонтальное расположение.

И тут я хочу вернуться к тому, что уже упоминал выше — в 2015 студии разработчики Microsoft почему-то отказались от своего «модного» нового вертикального окна статического анализа (на картинке слева) в пользу «старого доброго» Error List'а. Почему они так сделали? На мой взгляд, а также по опыту собственного использования этого нового окна — просто потому, что этим окном было неудобно пользоваться. В вертикальной таблице на экран влезало мало сообщений, не было возможности отсортировать их по какому-нибудь критерию (например, по коду или по файлу), а при «раскрытии» карточки с сообщением, оно вообще порою занимало весь экран.

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

После того, как мы определились с общим направлением, нужно было выбрать технологическую основу для интерфейса. Предыдущая версия нашего интерфейса использовала «старый добрый» Windows Forms — это было связано с необходимостью поддержки Visual Studio 2005 и 2008. С учётом того, что сам Visual Studio плагин разрабатывается на C#, Visual Studio не является кроссплатформенной и, наверное, главное — что сама Visual Studio использует технологию WPF, самым логичным кажется использовать тот же WPF и для обновлённого интерфейса. Тем не менее, даже несмотря на то, что вся внутренняя логика плагина и используемые им структуры данных напрямую на интерфейс не «завязаны», мы решили в текущей версии попробовать сначала провести «косметический ремонт», т.е. попросту «перекрасить» текущий интерфейс в соответствии с обновлённым дизайном, оставив все нижележащие компоненты без изменений. К сожалению, реальный мир диктует свои условия, и иногда приходится расставлять приоритеты в том, что хочется сделать, и том, что сделать нужно. Тем более, такой косметический ремонт можно сделать очень малыми силами и за короткий срок.

Нужно признать, что базовых возможностей WinForms нам уже начинает не хватать — даже такая, казалось бы, небольшая «косметическая» правка потребовала реализации в коде нескольких «костылей» — не заметных для пользователей, но уже затрудняющих дальнейшие поддержку и развитие интерфейса. Например, пришлось «повозиться» с новыми кнопками уровней (с полоской цвета) при отображении на toolstrip панели. Стандартный toolstrip в WinForms сам отвечает за отрисовку своих элементов, но при хостинге пользовательского контрола, отрисовку этого контрола приходится перекладывать на него самого (это было особенно актуально для нас в плане поддержки цветовых схем). Использование WinForms также вынуждает нас для поддержки цветовых схем пользоваться достаточно архаичным VS SDK интерфейсом GetVSSysColorEx, и самостоятельно «раскрашивать» наши компоненты, а также отлавливать момент переключения цветовой темы. Для WPF компонентов Visual Studio предлагает более удобный механизм подхвата цветов через динамические ресурсы, напрямую в XAML разметке.

В итоге, думаю, мы можем с большой долей уверенности утверждать, что следующая (после текущего обновления) версия интерфейса PVS-Studio будет полностью основана на WPF. Сейчас же, нашей целью, как и для прошлой итерации интерфейса, было сохранение «аутентичности» по отношению к стандартным окнам Visual Studio. Получившийся у нас результат можно увидеть ниже:

Рисунок 3 - обновлённый интерфейс PVS-Studio (окно вывода и главное меню)

Рисунок 3 — обновлённый интерфейс PVS-Studio (окно вывода и главное меню)

Пункты меню, иконки и грабли

Старые иконки плагина PVS-Studio были далеки от идеала, а точнее, от стандартной «темы оформления» в Visual Studio, поэтому было принято решение их заменить. Основным требованием для новых иконок было их соответствие единому стилю и хороший контраст для всех цветовых схем Visual Studio.

Определившись с сетом иконок, мы приступили к разработке. Первой проблемой, с которой мы столкнулись, стало, как ни странно, непосредственно отображение иконок в основном меню Visual Studio. Вы скажите: «Подождите, ведь вы уже показывали свои иконки в этом меню, начиная с первой версии PVS-Studio!». Действительно, это так, но используемый нами ранее метод не подходил для наших целей. Уточню, что для задания иконок мы использовали единый механизм расширений Visual Studio для создания пунктов меню — vsct файлы (про это можно подробнее почитать здесь). Что же нас не устраивало в этом варианте? Для начала, иконки можно было задать только один раз — при загрузке плагина студией. Мы же хотели имитировать поведение стандартных иконок студии, т.е. перекрашивать их в зависимости от выбранной цветовой схемы (как оказалось потом, это требование было излишним — студия умеет это делать сама). Во-вторых, vsct файл не умеет (или возможно мы не умеем в рамках vsct файла) использовать в изображении иконок альфа канал, из-за чего они выглядели весьма неприглядно.

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

var commandBars = (CommandBars)DTE.CommandBars;
var menuBar = commandBars[menuBarKey];
var pvsBar = menuBar.Controls["PVS-Studio"] 
    as CommandBarPopup;
...
cmdBtnControl.Picture = ImageHelper.BitmapToStdPicture(bmp);

Также у нас возникло беспокойство, что такой метод «сломается», если пользователь захочет (а почему бы и нет?) переименовать пункт меню PVS-Studio, ведь Visual Studio позволяет это делать. Однако, эти опасения оказались напрасными — внутреннее имя у объектов commandBar не меняется.

Решив вопрос с динамическим отображением иконок, мы реализовали алгоритм, по которому монохромная иконка закрашивалась бы в цвет, выбранный из активной темы Visual Studio. Мы инвертировали все иконки в ресурсах плагина в белый цвет и написали простой алгоритм преобразования, который умножал цвет каждого пикселя изображения на необходимый нам:

public static void Colorize(ref Bitmap img, Color color)
{
  for (int x = 0; x < img.Width; x++)
  {
    for (int y = 0; y < img.Height; y++)
    {
      // Обходим каждый пиксель изображения в цикле
      // И умножаем его цвет на цвет текста в текущей цветовой схеме
      var pixel = img.GetPixel(x, y);
      var r = (byte)(((float)pixel.R) * ((float)color.R / 255f));
      var g = (byte)(((float)pixel.G) * ((float)color.G / 255f));
      var b = (byte)(((float)pixel.B) * ((float)color.B / 255f));
      img.SetPixel(x, y, Color.FromArgb(pixel.A, r, g, b));
    }
  }
}

На выходе мы получали вот такой результат:

Рисунок 4 - Пример работы алгоритма колоризации иконок.

Рисунок 4 — Пример работы алгоритма колоризации иконок.

Однако, мы обнаружили неожиданное поведение — в случае выбора, например, «красной» темы оформления Visual Studio, иконка отображалась корректно. Корректно она отображалась и в «светлой» схеме. В «тёмной» же схеме иконки оставались чёрными, а не становились белыми, как мы ожидали. Оказалось, что студия сама умеет инвертировать определённые цвета картинки в зависимости от «яркости» фона в выбранной цветовой схеме — предыдущие эксперименты с колоризацией иконок оказались напрасными. Студия всё сделала за нас сама. Несмотря на это, мы всё-таки решили оставить описанный выше метод программного задания иконок из-за проблем с альфа-каналом.

Аналогично, в нашем окне вывода сообщений PVS-Studio мы поменяли все значки и иконки на подходящие из того же набора. Однако, если иконки в меню изменяли цвет в зависимости от цветовой схемы Visual Studio автоматически, то иконки в окне плагина оставались в исходном виде для всех цветовых схем.

Рисунок 5 - Проблемы с отображением иконок в темной цветовой схеме Visual Studio.

Рисунок 5 — Проблемы с отображением иконок в темной цветовой схеме Visual Studio.

Мы задались вопросом, можно ли использовать Visual Studio SDK для применения студийного алгоритма колоризации для наших иконок. Результат не заставил нас долго ждать. Спустя несколько минут поиска мы наткнулись на метод GetThemedBitmap в классе ImageThemingUtilities пространства имен Microsoft.VisualStudio.PlatformUI. Он делал именно то, что нам и было нужно!

Рисунок 6 - Корректное отображение иконок в нашем окне для тёмной темы Visual Studio.

Рисунок 6 — Корректное отображение иконок в нашем окне для тёмной темы Visual Studio.

Еще одна проблема, с которой мы столкнулись при изменении иконок — это их размытие на DPI отличном от 100%. Это происходило, поскольку все иконки имели размер 16х16 пикселей. Данную проблему нам удалось частично победить, используя изначально иконки размером 64x64. В таком случае VisualStudio автоматически их сжимала до нужного размера. Но и в данном варианте все оказалось не совсем гладко. Теперь при 100% DPI появилось небольшое размытие. В результате было принято решение использовать именно изображения размером 64x64, так как на DPI отличных от 100% это давало наиболее приемлемый результат, а при 100% DPI, размытие было практически не заметным.

Так же при DPI в 115% было замечено обрезание правого края иконки на несколько пикселей. Попытки программно добавлять несколько пикселей справа (чтобы изображение имело размер 64x68) тоже не увенчались успехом. Иконки в Visual Studio сжимались обратно до квадратного соотношения сторон, и хоть обрезание и удалось победить, но размытие выросло в разы. В итоге мы просто добавили несколько пустых пикселей в каждую иконку, оставив размер 64х64. Это решение обеспечило минимальное размытие (практически незаметное для глаз) и исключило обрезание правой стороны у иконок.

Заключение

Несмотря на кажущиеся значительные внешние изменения, «под капотом» интерфейс PVS-Studio фактически остался прежним. Мы надеемся, что для наших старых пользователей новый интерфейс останется «знакомым», а для новых пользователей он будет более интуитивно понятен и приятен. Ну и конечно, что он будет «красивее» в глазах всех наших пользователей. Насколько у нас это получилось — судить вам.

Автор: PVS-Studio

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js