Pull to refresh

Рисуем виджеты в заголовке окна

Reading time 3 min
Views 11K
Ни для кого не секрет, сегодня у населения в основном преобладают широкоформатные мониторы и они вынуждены экономить количество пикселей по вертикали. Это породило моду на рисование элементов управления прямо в заголовке окна. Сейчас этим уже никого не удивишь, но, тем не менее, нигде ещё не находил решения этой проблемы на Qt, поэтому сейчас мы будем это исправлять:



В тёмных застенках нашей qutIM'овской лаборатории родился класс ToolFrameWindow, который во многом подобен таким классам, как QMainWindow и QToolBar. WinAPI мытарства я описывать не буду, не хочется загромождать статью. Чтобы добиться этого эффекта необходимо расширить клиентскую зону на всё окно и руками обрабатывать позицию курсора, чтобы можно было перемещать и ресайзить окошко. При этом пропадает прозрачность, для её возращения нам помогает QtDWM.

Далее я хотел бы описать принцип его использования:


Первым делом нужно задать ему виджет, для которого мы будем рисовать в заголовке кнопки, делается это через вызов метода setCentralWidget

ToolFrameWindow w;
Form form;
w.setCentralWidget(&form);


* This source code was highlighted with Source Code Highlighter.



Сделано это таким образом, чтобы максимально легко обеспечить перенос уже существующего кода и не создать затруднений на других платформах.

Добавление действий:

Кнопки в заголовок добавляются при помощи метода addAction, так-же можно добавлять пустое пространство addSpace или же добавлять разделитель addSeparator.

QAction action(&w);
w.addSpace(16);
w.addAction(&action);
w.addSeparator();


* This source code was highlighted with Source Code Highlighter.



Добавление табов, кнопок, надписей и других виджетов:


Специально для добавления всего этого безобразия был предусмотрен метод addWidget, при добавлении можно указать выравнивание через Qt::Aligment, что позволяет делать кнопки как на скриншоте. Что касается самой кнопки, то она сделана из QPushButton'а при помощи qss стилей, в частности очень помог border-image

  QPushButton btn;
  btn.setIcon(QIcon(":/ubuntu.png"));
  btn.setStyleSheet("QPushButton { "
           "border: 5px;"
           "border-image: url(:/button.png);"
           "}");
  btn.setMinimumSize(64, 22);
  btn.setMaximumSize(64, 22);


* This source code was highlighted with Source Code Highlighter.


Для рисования свечения под текстом я попробовал использовать QGraphicsDropShadowEffect, но сила свечения оказалась слишком маленькой. Это привело к некоторому замешательству, но я вспомнил про старый добрый border-image и решил сделать ход конём: отрисовать свечение как картинку и наложить её как border-image

  QLabel lbl(QObject::tr("Glow caption. Text with glow"));
  lbl.setStyleSheet("QLabel {border-image: url(:/background.png);border: 5px; }");


* This source code was highlighted with Source Code Highlighter.


Получилось не совсем идеально, есть ещё куда стремиться в подборе правильных параметров и размеров свечения, но в целом это решение является достаточно простым и при этом гибким.

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

Заключение


Один небольшой скриншот:

Самые смелые могут попробовать этот плагин. Но он ещё откровенно не дописан, о нем разговор будет позже.

Огромное спасибо dtf за войну с APIлками и за реализацию всех winAPI методов. Код библиотеки доступен в gitorious'е (качаем тег habrahabr). Лицензия на данный момент GPLv2. Для использования в своем коде достаточно скопировать нужные файлы в свой проект, тестовая программа находится в каталоге wincaption.
Tags:
Hubs:
+94
Comments 41
Comments Comments 41

Articles