Ошибки GTK+ Java lnf для Swing на Linux/Unix

или почему MagicPlot использует Metal look-and-feel на *nix-системах

По умолчанию любая программа, написанная на Java и основанная на графической библиотеке Swing, отображается с использованием стандартного look-and-feel (skin) под названием Metal. Metal разрабатывался в 90-х и сейчас считается устаревшим. Он имеет упрощенные формы, при этом быстро работает, но создает у пользователя не слишком хорошее впечатление в сравнении с внешним видом других программ. На замену стандартному look-and-feel можно включить системный, который очень точно копирует внешний вид и поведение компонентов операционной системы.

Системный look-and-feel под Windows и Mac OS X выглядит очень убедительно и работает достаточно гладко. На большинстве Linux/Unix компьютеров системным look-and-feel в Java является GTK+. Когда я увидел GTK+ look-and-feel на линуксе (Ubuntu 9), мое первое впечатление было положительным. Но в последствии выяснилось, что не смотря на кажущуюся красоту, по вине разработчиков нормально работать с GTK+ look-and-feel не представляется возможным. В этой заметке я приведу некоторые замеченные мной глюки, наличие которых привело к тому, что мне пришлось "вручную" отключить GTK+ L&F в MagicPlot.

Disclimer: Все написанное ниже есть лишь мои наблюдения как программиста, который занимается разработкой программы для конечного пользователя, которого не интересует, чьи где глюки (мои или каких-либо библиотек). Вполне возможно, что проблемы, которые я описываю, устранимы если глубже вникнуть, но я этого не сделал, поскольку был занят чем-то более важным и/или полезным.

Описанные особенности имеют место как в Oracle/Sun HotSpot JRE, так и в OpenJDK JRE версий 1.6_с_чем-то.

1. Сетка таблицы включена, но не отображается

Ответ оказался прост: цвет сетки по умолчанию в GTK+ был белый!

Лечение: замена цвета сетки на серый:

UIManager.put("Table.gridColor", Color.gray);

2. Не отображаются разделители в меню

Это вообще необъяснимо, учитывая то, что этот глюк описан даже в инструкции по установке среды разработки NetBeans на Линукс. Разработчики NetBeans советуют в случае, если отсутствие разделителей раздражает, сменить look-and-feel на другой.

Также весьма странно отображаются неактивные пункты, например, пункт Export Table на этом скриншоте.

Лечение: не найдено.

3. Бегунок JSlider показывает значение в виде числа


Особенно смешно выглядит при вертикальном расположении.
Это регулятор зума холста, который можно менять от 1х до 4х с мелким шагом. Поскольку JSlider — целочисленный, я пересчитываю вещественный диапазон 1.0...4.0 в целочисленный 0...1000 и обратно, и потому число на
JSlider не имеет смысла

Разработчики GTK вновь решили осчастливить пользователей (или меня?) и добавили к бегунку поле, в котором выводится числовое значение. Хм. Выглядит, конечно, симпатично. Но... Это плохо как минимум по следующим причинам:

  1. Нестандартный, не ожидаемый программистами внешний вид.
  2. Поле с числом занимает дополнительное место, и бегунок имеет нестандартный размер. Композиция окна, задуманная программистом, нарушается.
  3. Числовое значение бегунка не всегда имеет "физический смысл", и даже когда такой смысл имеется, не всегда значение следует выводить именно в таком виде. Пример — регулировка громкости в децибелах: числовой диапазон и значение бегунка может не иметь ничего общего с тем, что думает пользователь. Особенно это актуально при задании вещественных величин: JSlider может работать только с целыми числами и для малой дискретности приходится задавать большой диапазон целочисленных значений и пересчитывать в вещественные делением. Другими словами, иногда программист не хочет показывать числовое значение бегунка пользователю, а тут его раскрывают без спроса.

Лечение: отключение этого свинского свойства:

UIManager.put("Slider.paintValue", Boolean.FALSE);

4. Выпадающий список JComboBox выдает странный PreferredSize


Так оно выглядит в GTK+. Я не знаю, как объяснить такой вид пользователям


Metal. Как должно быть в моем понимании (кхм, числа разные, кхм, ну ладно)


Windows look-and-feel

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

Лечение: не найдено.

5. Автоматически включенная "панель задач" в JDesktopPane


Верхние кнопки мои, нижние — от GTK+

В JDesktopPane нет кнопок, позволяющих удобно переключать окна. Я решил восполнить этот пробел и реализовал свою "панель задач". Она позволяет не только переключать окна, но и закрывать их средней клавишей мыши подобно вкладкам. Еще я добавил кнопку Windows, в которой разместил пункты меню Close All, Windows Cascade. Я добавил контекстное меню к кнопкам окон, в котором сделал среди прочих важный пункт "Close All Other".

Каково же было мое удивление, когда в GTK+ я увидел сразу две "панели задач"! Естественно, "панель задач", которую рисует GTK+ не имеет тех возможностей, которые предусмотрел я.

Лечение: отключить "панель задач":

UIManager.put("InternalFrame.useTaskBar", Boolean.FALSE);

6. По виду JToggleButton сложно понять, нажата ли она

См. скриншот с "панелью задач". Кнопка Figure 2 нажата, остальные — нет, пойди отличи. Пользователи будут рады такому новому нововведению.

7. Невозможно полностью отключить рамку и задать прозрачность текстового поля


Результат на GTK+


Metal: как должно быть


Nimbus: снова паразитный фон


Windows


Mac OS X native

В данном случае речь идет о строке состояния, элементы которой по задумке не должны иметь рамки и фона. Вызовы, приведенные ниже, работают везде кроме GTK+ и Nimbus. Даже на Mac OS X работает:

field.setOpaque(false);
field.setBorder(BorderFactory.createEmptyBorder());

На Nimbus рамка отключается, но фон все равно остается.

Лечение: не найдено. Имеется похожий баг для JButton.

8. Не работает отображение иконок в JTable

При попытке вернуть иконку в качестве содержимого ячейки таблицы происходит исключение и в окне ничего не рисуется:

java.lang.NullPointerException
        at javax.swing.JTable.prepareRenderer
        at javax.swing.plaf.synth.SynthTableUI.paintCell
        at javax.swing.plaf.synth.SynthTableUI.paintCells
        at javax.swing.plaf.synth.SynthTableUI.paint
        at javax.swing.plaf.synth.SynthTableUI.update
        at javax.swing.JComponent.paintComponent
...

Все описано в этом баге. Кстати, у Nimbus look-and-feel такое же поведение (то есть виноват Synth)!

9. Критические ошибки при использовании некоторых скинов GTK

У GTK+ еще есть собственные темы оформления ("скины"). И в зависимости от того, какая тема выбрана, программа будет работать... Или не работать.

Например, при оформлении по умолчанию из Ubuntu 10 (Ambiance) попытка открыть в программе внутреннее окно в JDesktopPane приводит к исключению:

java.lang.NullPointerException
    at com.sun.java.swing.plaf.gtk.Metacity.getInt
    at com.sun.java.swing.plaf.gtk.Metacity.calculateButtonSize
    at com.sun.java.swing.plaf.gtk.Metacity$TitlePaneLayout.minimumLayoutSize
    at com.sun.java.swing.plaf.gtk.Metacity$TitlePaneLayout.preferredLayoutSize
    at java.awt.Container.preferredSize
   ...

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

Лечение: не найдено, возможно проблема решена в новой версии (которая установлена не у всех!).

Заключение

Разработчики GTK+ look-and-feel в Swing хотели как лучше, но получилось как всегда. Я допускаю, что некоторые глюки, например, с отсутствием разделителей в меню и размерами выпадающих списков, на самом деле могут быть тяжело преодолимы, и, возможно, даже как-то связаны с самой библиотекой GTK+ и прочее. Но я не понимаю, зачем включать по умолчанию нестандартные режимы в JSlider, JDesktopPane и тому подобное. Сделать удобнее и красивее — можно, но в режиме по умолчанию объекты должны вести себя стандартным образом.

Я потратил значительное время на поиски способов отключить нестандартное поведение. Отмечу, что все эти возможности нигде не задокументированы. По крайней мере, я не смог найти официальное описание в Интернете за разумное время.

В итоге на *nix системах в MagicPlot используется стандартный Metal, а на Windows и Mac OS X — системный look-and-feel.

Кстати, для тех, кто ищет, вот тут есть полный список всех UI defaults свойств компонентов Swing для разных look-and-feel.

И еще. К сожалению, в некоторых областях Nimbus идет по тому же пути, что и GTK+ look-and-feel. Печально.

Дополнение

Еще один обнаруженный мной баг относится уже к работе Metal в OpenJDK 1.6.0_18 на Линуксе. Я заменил жирные шрифты по умолчанию на нежирные (через UI defaults). После этого по непонятной мне причине странно работает вычисление размера кнопок и надписей, в результате чего в некоторых случаях текст на них обрезается и дополняется многоточием:

Текст на конопке Apply обрезан. Слева от кнопки — Box.createHorizontalGlue()

Как это исправить я пока не нашел. То, что надпись на кнопках может сокращаться — для меня очень большая новость. Не вижу тут логики. Если не трогать шрифты, и оставить жирные, то все работает.


Electriq Wednesday 06 October 2010 at 10:29 am | | Russian, Java
Used tags: , ,

two comments

pat

В OS X Swing тоже выглядит не совсем нативно. Вообще clearlook решает многие проблемы с GTK LAF

pat, - 06-11-’10 08:41
<span class='registered'>Electriq</span>

Ну по поводу нативности у меня такая позиция: нативность, конечно, лучше всего, но в первую очередь оформление интерфейса должно быть логичным (и работать, конечно). В примере с GTK LNF мы видим, что некоторые вещи просто не работают (вызывают исключения), а некоторые нелогничны (например, незаметность нажатой кнопки по сравнению с ненажатой).
В OS X есть свои примочки для Swing, которые можно использовать, а можно не использовать. В MagicPlot, например, меню программы вынесено из окна, как у нативных программ, хотя по умолчанию этого не происходит.

Electriq, (URL) - 06-11-’10 12:39
(optional field)
(optional field)
Remember personal info?
Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.