Реализация стилей и виджетов, поддерживающих стили
ВведениеСтили (классы, наследуемые от QStyle) рисуют от имени виджетов и инкапсулируют внешний вид и поведение графического интерфейса пользователя. В Qt встроено несколько стилей (например, стили windows и motif). Другие стили доступны только на определенных платформах (например, стиль windows XP). Пользовательские стили доступны как подключаемые модули (plugins) или через создание в приложении экземпляра класса стиля и установку его с помощью QApplication::setStyle(). Чтобы реализовать новый стиль создайте наследника одного из имеющихся стилей Qt - наиболее похожего на создаваемый вами стиль - и реализуйте заново несколько виртуальных функций. Поскольку этот процесс несколько запутан мы предоставляем этот обзор. Мы предоставим пошаговый разбор применения стиля к отдельным виджетам Qt. Мы рассмотрим виртуальные функции QStyle, переменные-члены и перечисления. Часть данного документа, что не имеет отношения к применению стиля к отдельным виджетам, следует читать последовательно поскольку следующие разделы имеют тенденцию зависеть от предыдущих. Описание виджетов может быть использовано как описание пока реализуется стиль. Как бы там ни было, в отдельных случаях вы можете обратиться за справкой к исходному коду Qt. Последовательность в процессе создания стиля (styling process) должна проясниться после прочтения этого документа, что поможет в локализации существенного (relevant) кода. Для разработки виджетов, поддерживающих стили (т.е. виджетов, которые согласовываются со стилем, в котором отрисовываются), вам необходимо отрисовывать их используя текущий стиль. Данный документ покажет как виджеты отрисовывают себя и какие возможности дает им стиль. Реализация QStyleAPI QStyle содержит отрисовывающие виджеты функции, статические вспомогательные функции (static helper functions) для общих и сложных задач (например, вычисления позиций рукояток ползунка) и функций для разнообразных вычислений необходимых во время отрисовки (например, для вычисления собственных размеров виджетов). Стиль также помогает некоторым виджетам размещать их содержимое. В дополнение, он создает QPalette содержащий несколько QBrush для отрисовки. QStyle отрисовывает графические элементы; элемент - это виджет или часть виджета подобно фаске кнопки нажатия, фрейму окна или полосе прокрутки. Когда виджет запрашивает стиль для отрисовки элемента, он предоставляется стилем с опцией стиля, который является классом, содержащим необходимую для отрисовки информацию. В данном разделе мы будем рассматривать элементы стиля, опции стиля и функции QStyle. В заключение, мы опишем использование палитры. Not all drawing is performed by the QStyle implementation; the items in item views are drawn by delegates (classes that inherit QAbstractItemDelegate). Заголовки отображений элементов отрисовываются стилем неподвижными. In this document we only describe how to implement a QStyle subclass, so we do not treat item view related parts of the drawing. In order to get absolute control over the painting of widgets, you need to implement your own delegates for the item views. Обратите внимание, что делегаты должны быть установлены программно для каждого отдельного виджета (т.е., стандартные делегаты не могут быть предоставлены как плагины (plugins)). This architecture has mainly been chosen for performance reasons. Элементы стиляЭлемент стиля - графическая часть графического интерфейса пользователя. Виджет состоит из иерархии (или дерева) элементов стиля. Например, когда стиль получает требование нарисовать кнопку нажатия (из QPushButton, например), он отрисовывает метку (текст и пиктограмму), кромку кнопки и прямоугольник фокуса. Кромка кнопки, в свою очередь, состоит из фрейма вокруг кромки с двух других элементов, которые будут отображаться позднее. Ниже приведена схематичная иллюстрация дерева элементов кнопки нажатия. Мы увидим реальное дерево для QPushButton когда перейдем к отдельным виджетам. Виджеты необязательно отрисовывать запрашивая стиль для отрисовки одного элемента. Виджеты могут сделать несколько вызовов стиля для отрисовки разных элементов. Примером является QTabWidget, который отрисовывает свои вкладки и фреймы по отдельности. Имеются три типа элементов: простейшие элементы, управляющие элементы и сложные управляющие элементы. Элементы определены перечислениями ComplexControl, ControlElement и PrimitiveElement. Значения каждого элементе перечисления имеет префикс для обозначения его типа: CC_ для сложных элементов, CE_ для элементов управления и PE_ для простейших элементов. В следующих трех разделах мы увидим, что определяют разные элементы и увидим примеры использующих их виджетов. Описание класса QStyle содержит список этих элементов и их роли в стайлинге виджетов (styling widgets). Мы увидим как их использовать когда применим стиль к отдельным виджетам. Простейшие элементыПростейшие элементы - это общие и часто используемые несколькими виджетами элементы графического интерфейса пользователя. Примером являются фреймы, кромки кнопок и стрелки для окошек счетчиков, полосы прокрутки и комбинированные списки выбора. Простейшие элементы не могут существовать сами по себе: они всегда являются частью более крупных структурных компонентов. Они не принимают участия во взаимодействии с пользователем, но являются пассивными украшениями графического интерфейса пользователя. Элементы управленияЭлемент управления выполняет действие или отображает информацию пользователю. Примеры элементов управления - кнопки нажатия, флажки и заголовки секций в табличных и древовидных представлениях. Элементы управления необязательно являются завершенными виджетами как, например кнопки нажатия, но могут также быть частью виджетов таких как вкладки набора вкладок (tab bar) и ползунки полосы прокрутки. Они отличаются от простейших элементов тем, что не являются пассивными, но выполняют функцию во взаимодействии с пользователем. Элементы управления, состоящие из нескольких элементов, часто используют стиль для вычисления ограничивающих элементы прямоугольников. Доступные субэлементы определены в перечислении SubElement. Это перечисление используется только для вычисления ограничивающих прямоугольников и субэлементы не являются такими же графическими элементами способными быть отрисованными как простейшие, управляющие и сложные элементы. Сложные элементы управленияСложные элементы управления содержат субэлементы управления (sub controls). Поведение сложных элементов управления по-разному зависит от того, где пользователь воздействовал на них с помощью мыши и какую клавишу нажал. Зависит это от субэлемента управления (или нескольких), над которыми находится мышь или получено нажатие мыши. Примеры сложных элементов управления - это полосы прокрутки и комбинированные списки выбора. С полосой прокрутки вы можете использовать мышь для перемещения ползунка и нажимать на кнопки сдвига на одну строку вверх и вниз. Доступные субэлементы определены в перечислении SubControl. В дополнение к отрисовке, стиль обязательно предоставляет виджеты с информацией в каком субэлементе (или нескольких) было сделано нажатие мыши. Например, QScrollBar нуждается в знании того, нажал ли пользователь на ползунок, бороздку ползунка или одну из кнопок. Обратите внимание, что субэлементы - это не то же, что управляющие элементы, описываемые в предыдущем разделе. Вы не можете использовать стиль для отрисовки субэлементов управления; стиль будет только вычислять ограничивающий прямоугольник в котором субэлементы управления должны быть отрисованы. Тем не менее обычно сложные элементы управления используют элементы управления и простейшие элементы для отрисовки свои субэлементов, что часто используется встроенными стилями Qt, а также стилем Java. Например, стиль Java использует PE_IndicatorCheckBox для отрисовки независимого переключателя в контейнерах (которые являются субэлементами CC_GroupBox). Некоторые субэлементы управления имеют эквивалентный элемент управления, например, ползунок полосы прокрутки (SC_SCrollBarSlider и CE_ScrollBarSlider). Другие задачи QStyleЭлементы стиля и виджеты, как упоминалось, используют стилья для вычисления ограничивающих прямоугольников субэлементов и субэлементов управления, и пиксельные метрики (pixel metrics), которые являются зависящими от стиля размерами экрана в пикселях, для масштаба во время отрисовки. Доступные прямоугольники и пиксельные метрики в QStyle представляются тремя перечислениями: SubElement, SubControl и PixelMetric. Значения перечислений могут быть легко идентифицированы, т.к. они начинаются на SE_, SC_ и PM_. Стиль содержит также набор подсказок для стиля (style hints), которые представляют значения из перечисления StyleHint. Все виджеты обладают разной функциональностью и внешним видом в разных стилях. Например, когда пункты меню не помещаются в один столбец в меню на экране, некоторые стили поддерживают прокрутку, в то время как другие отрисовывают более одного столбца для размещения всех пунктов. Стиль обычно обладает набором стандартных изображений (таких как предупреждение, вопрос и картинка ошибки) для окон сообщений, файловых диалогов и т.д., предоставляемых в QStyle перечислением StandardPixmap. Его значения представляют стандартные изображения. Виджеты Qt используют их, так что когда реализуете пользовательский стиль добавьте изображения, используемые реализуемым стилем. Стиль вычисляет промежуток между виджетами в компоновках. Существует два способа обработки этих вычислений стилем. Вы можете установить PM_LayoutHorizontalSpacing и PM_LayoutVerticalSpacing, так делает стиль java style (через QCommonStyle). В качестве альтернативы, вы можете реализовать QStyle::layoutSpacing() и QStyle::layoutSpacingImplementation() если необходим больший контроль за частями компоновки. В этих функция вы можете вычислить промежутки, основанные на типах управления (QSizePolicy::ControlType) для различных политик размеров (QSizePolicy::Policy), а также опции стиля для данного виджета. Опции стиляОпция стиля (класс, наследуемый от QStyleOption) хранит параметры, используемые функциями QStyle. Подклассы QStyleOption содержат всю информацию, необходимую стилю отдельных виджетов. Опции стиля хранят общие переменные по причинам производительности. Опции стиля заполняются виджетами. Виджеты могут находиться в одном из нескольких состояний, которые определяются перечислением State. Некоторые флаги состояния (state flags) имеют различное значение в зависимости от виджета, но остальные - общие для всех виджетов, подобно State_Disabled. QStyleOption устанавливает общие состояния с помощью QStyleOption::init(); остальные состояния устанавливаются отдельными виджетами. Весьма заметно, опции стиля содержат палитру и ограничивающие прямоугольники для отрисовки виджетов. Многие виджеты имеют специализированные опции стиля. Например, QPushButton и QCheckBox, используют QStyleOptionButton как опцию стиля, которая содержит текст, пиктограмму и размер пиктограммы. Точное содержимое всех опций будет описано когда мы рассмотрим отдельные виджеты. Функции QStyleКласс QStyle определяет три функции для отрисовки простейших элементов, элементов управления и сложных элементов управления: drawPrimitive(), drawControl() и drawComplexControl(). Функции принимают следующие параметры:
Не все виджеты посылают указатель на себя. Если опция стиля, посылаемая в функцию, не содержит необходимую вам информацию, проверьте реализацию виджета чтобы посмотреть отсылает ли он указатель на самого себя. Класс QStyle также предоставляет вспомогательные функции, которые используются при отрисовке элементов. Функция drawItemText() отрисовывает текст внутри заданного прямоугольника и принимает в качестве параметра QPalette. Функци drawItemPixmap() помогает выравнять растровое изображение (pixmap) внутри заданного ограничивающего прямоугольника. Другие функции QStyle выполняют различные вычисления для функций отрисовки. Виджеты также используют эти функции для вычисления подсказок размеров (size hints), а также для вычислений ограничивающих прямоугольников если они отрисовывают несколько элементов стиля самостоятельно. Также как и функции отрисовки элементов вспомогательные функции принимают несколько аргументов.
QStyle также имеет функции polish() и unpolish(). Все виджеты отправляют в функцию polish() перед тем как показать их и unpolish() - когда они невидимы. Вы также можете использовать эти функции для установки атрибутов виджетов или выполнения другой работы, которой требует ваш стиль. Например, если вам необходимо узнать когда мышь нависнет над виджетом, вам необходимо установить атрибут виджета WA_Hover. Тогда флаг состояния State_MouseOver будет установлен в опциях стиля виджета. QStyle обладает также несколькими статическими вспомогательными функциями, которые выполняют несколько общих и трудных задач. Они могут вычислить позицию ползунка из значения ползунка и трансформировать прямоугольники и отрисованный текст с учетом обратных компоновок; за подробностями обращайтесь к документации класса QStyle. Обычный метод, когда заново реализуются виртуальный функции QStyle, заключается в работе на элементах, которые отличаются от суперкласса; для остальных элементов вы можете просто использовать реализацию суперкласса. ПалитраКаждый стиль предоставляет цвет - то есть, QBrush - палитру, которая будет использована для отрисовки виджетов. Для разных состояний виджета имеется свой набор цветов (QPalette::ColorGroup): активный (виджеты в окне, которое в фокусе клавиатуры), неактивный (виджеты используются для других окон) и отключенный (виджеты, которые отключены). Состояния можно найти опросив флаги состояния State_Active и State_Enabled. Каждый набор содержит цвет определенных ролей, задаваемых перечислением QPalette::ColorRole. Роли описывают в каких ситуациях цвета будут использованы (например, для отрисовки фона виджета, текста или кнопок). Как роли цвета (color roles) используются определяется стилем. Например, если стиль использует градиенты, то для создания градиента можно использовать цвет из палитры и сделать его темнее или светлее с помощью QColor::darker() и QColor::lighter(). В общем, если в палитре не нужной кисти, попробуйте породить ее из имеющейся. QPalette, который предоставляет палитру, хранит цвета для различных состояний виджетов и ролей цвета. standardPalette() возвращает палитру для стиля. Стандартная палитра не устанавливается автоматически когда новый стиль устанавливается на приложение (QApplication::setStyle()) или виджет (QWidget::setStyle()), так что вы должны установить палитру самостоятельно с помощью (QApplication::setPalette()) или (QWidget::setPalette()). Не рекомендуется жестко кодировать цвета, поскольку приложения и отдельные виджеты могут устанавливать собственные палитры и использовать палитры стилей для отрисовки. Обратите внимание, что ни один виджет Qt не устанавливает свою собственную палитру. Стиль java жестко кодирует некоторые цвета, но автор его внешнего вида оставил это в забвении. Конечно, это не значит что стиль будет выглядеть хорошо с любой палитрой. Проблемы реализацииПри реализации стилей имеется для рассмотрения несколько проблем. Здесь мы дадим несколько намеков и совет по реализации. Когда реализуется стиль необходимо просмотреть код виджетов и код базового класса и его предков. Это потому, что виджеты используют стиль по-разному, потому, что реализация виртуальных функций в разных стилях может оказывать влияние на состояние отрисовки (например, изменение состояния QPainter без восстановления и отрисовка некоторых элементов без использования подходящих пиксельных метрик и субэлементов). Рекомендуется, чтобы стили не меняли предлагаемый с помощью функции QStyle::sizeFromContents() размер виджетов, но позволяли реализации QCommonStyle обрабатывать его. Если необходимо внести изменения попытайтесь сделать их минимальными; разработка приложения усложнится, если для разных стилей компоновка виджетов значительного различается. Мы рекомендуем использовать непосредственно QPainter для отрисовки, т.е. не использовать пиксельные отображения или изображения. Это облегчит согласование стиля с палитрой (хотя вы можете установить собственную таблицу цветов в QImage с помощью setColorTable()). Разумеется, возможно отрисовывать элементы и без использования стиля для отрисовки субэлементов как спроектировано Qt. Это мешает поскольку пользовательские виджеты могут зависеть от корректной реализации этих субэлементов. Критический разбор виджетов покажет как Qt использует субэлементы. Стиль JavaМы реализовали стиль, имеющий сходство с внешним видом и поведением по умолчанию Java (известный до этого как Metal). Мы сделали это так как это было сравнительно просто реализовать, а мы хотели создать стиль для данного обзорного документа. Для сохранения его простым и негромоздким, мы кое-что упростили в стиле, но с Qt отлично можно сделать точную копию стиля. Однако, конкретных планов по реализации стиля как части Qt нет. В данном разделе мы рассмотрим некоторые проблемы реализации. В заключение, мы рассмотрим завершенный пример применения стиля Java к виджету. Мы продолжим использовать стиль java на всем протяжении документа в примерах и изображениях виджетов. Сама по себе реализация немного запутана и не предназначена для чтения вами. Проектирование и реализацияПервым шагом при разработке стиля является выбор базового класса. Мы выбрали подкласс QWindowsStyle. Данный класс реализуется большую часть необходимой функциональности для выполнения текущего рисования. Также, окна и java разделяют компоновку субэлементов управления для нескольких сложных элементов управления (которые значительно уменьшают объем кода). Стиль реализован в одном классе. Мы сделали так поскольку нашли удобным хранить весь код в одном файле. Также выгодно это из-за пожеланий оптимизации так как мы подвергнем обработке меньше объектов. Мы также оставим количество функций минимальным используя ключи для идентификации элемента, который отрисовывается функциями. Результат в больших функциях, но так как мы разделили код для каждого элемента по ключам, читать код будет легко. Ограничения и отличия от JavaМы не реализовали полностью каждый элемент из стиля Java. Таким образом, мы уменьшили величину и сложность кода. Вообще, стиль планировался как практический пример для обзорного документа по стилям, а не как части самой Qt. Не у всех виджетов реализованы каждое состояние. Это сделано для общих состояний, например, State_Disabled. Каждое состояние, однако, реализовано как минимум для одного виджета. Мы реализовали только метки ниже ползунка. Плоские кнопки (flat push buttons) также пропущены. Мы не обрабатываем случай, когда строки заголовков и заголовки присоединяемых окон становятся меньше содержимого, просто отрисовываем субэлементы управления один за другим. Мы не пытались эмулировать шрифты Java. Java и Qt используют очень разные механизмы шрифтов (font engines), так что мы не рассматривали его ценность поскольку мы использовали стиль только для примера в этом обзоре. Мы жестко закодировали цвета (QPalette не использовалась) для линейных градиентов, используемых, например, для кромок кнопок, панелей инструментов и флажков. Это потому, что палитра Java не может выводить такие цвета. В любом случае Java не меняет эти цвета, основанные на цветовой группе виджета или роли (они не зависят от палитры), так что при любых обстоятельствах это не представляет проблемы. Это виджеты Qt, к которым можно применить стиль. Некоторые виджеты совсем не существует в Java, например, QToolBox. Другие содержат элементы, которых нет в виджетах Java. Виджет-дерево - это пример последнего, в нем JTree из Java не имеет заголовка. Стиль не обрабатывает обратные компоновки (reverse layouts). Мы предполагаем, что направление компоновки (layout direction) слева на право. QWindowsStyle обрабатывает обратные виджеты (reverse widgets); если мы реализовали обратные компоновки, виджеты, у которых мы сами изменили положение субэлементов или трогали выравнивание текста в метках, необходимо обновить. Моделирование флажков JavaВ качестве примера, мы рассмотрим моделирование флажков в стиле java. Мы опишем полный процесс и опубликуем весь код и стиля java и связанных классов Qt. In the rest of this document we will not examine the source code of the individual widgets. Надеемся, это даст вам идею как нужно разыскивать конкретные детали реализации в коде; большинство виджетов придерживаются структуры, схожей с флажками. Мы отредактировали код QCommonStyle, удалив из него кое-что не относящееся напрямую к моделированию флажков. Мы начнем с рассмотрения построения QCheckBox опции стиля, которая для флажков является QStyleOptionButton: opt.init(q); if (down) opt.state |= QStyle::State_Sunken; if (tristate && noChange) opt.state |= QStyle::State_NoChange; else opt.state |= checked ? QStyle::State_On : QStyle::State_Off; if (q->testAttribute(Qt::WA_Hover) && q->underMouse()) { if (hovering) opt.state |= QStyle::State_MouseOver; else opt.state &= ~QStyle::State_MouseOver; } opt.text = text; opt.icon = icon; opt.iconSize = q->iconSize(); Сначала мы разрешим установить QStyleOption опцию с информацией с init(), общей для всех виджетов. Рассмотрим это кратко. Нижнее булево равно true когда пользователь нажал прямоугольник вниз; это верно если квадрат отмечен или при отрицании флажка. Состояние State_NoChange устанавливается, когда мы имеем флажок с тремя состояниями и он частично отмечен. Имеется State_On если квадратик отмечен и State_Off - если не отмечен. State_MouseOver устанавливается, если мышь наведена на флажок и атрибут виджета Qt::WA_Hover установлен - устанавливается в QStyle::polish(). В дополнение, опция стиля также содержит текст, пиктограмму и размер пиктограммы кнопки. init() устанавливает опцию стиля с атрибутами, общими для всех виджетов. Мы печатаем реализацию здесь: state = QStyle::State_None;
if (widget->isEnabled())
state |= QStyle::State_Enabled;
if (widget->hasFocus())
state |= QStyle::State_HasFocus;
if (widget->window()->testAttribute(Qt::WA_KeyboardFocusChange))
state |= QStyle::State_KeyboardFocusChange;
if (widget->underMouse())
state |= QStyle::State_MouseOver;
if (widget->window()->isActiveWindow())
state |= QStyle::State_Active;
#ifdef Q_WS_MAC
extern bool qt_mac_can_clickThrough(const QWidget *w); //qwidget_mac.cpp
if (!(state & QStyle::State_Active) && !qt_mac_can_clickThrough(widget))
state &= ~QStyle::State_Enabled;
#endif
#ifdef QT_KEYPAD_NAVIGATION
if (widget->hasEditFocus())
state |= QStyle::State_HasEditFocus;
#endif
direction = widget->layoutDirection();
rect = widget->rect();
palette = widget->palette();
fontMetrics = widget->fontMetrics();
State_Enabled установлен когда виджет активирован. Когда виджет в фокусе - флаг State_HasFocus установлен. Равно, флаг State_Active установлен когда виджет является потомком активного окна. State_MouseOver будет установлен только если виджет имеет установленным флаг WA_HoverEnabled. Уведомляем, что навигация на дополнительной клавиатуре должна быть задействована в Qt для включения State_HasEditFocus; по умолчанию оно не включено. В дополнение к установке флагов состояния QStyleOption содержит другую информацию о виджете: direction - направление компоновки, rect - ограничивающий прямоугольник виджета (область, в которой отрисовывается), palette - QPalette, которая будет использована для отрисовки виджета, и fontMetrics - метрики для шрифта, используемого виджетом. Мы даем изображение флажка и опции стиля соответствующую ему. Флажок выше имеет следующие флаги состояния в опции стиля:
QCheckBox раскрашивает себя в QWidget::paintEvent() с помощью опции стиля opt и QStylePainter p. Класс QStylePainter - удобный класс для отрисовки элементов стиля. Весьма заметно, он обертывает методы в QStyle, используемые для рисования. QCheckBox отрисовывает себя как изложено ниже: QStylePainter p(this); QStyleOptionButton opt = d->getStyleOption(); p.drawControl(QStyle::CE_CheckBox, opt); QCommonStyle обрабатывает элемент CE_CheckBox. QCheckBox имеет два субэлемента: SE_CheckBoxIndicator (индикатор отметки) и SE_CheckBoxContents (содержимое, используемое для метки флажка). QCommonStyle также реализует эти ограничивающие прямоугольники субэлементов. Посмотрим на код QCommonStyle: QStyleOptionButton subopt = *btn; subopt.rect = subElementRect(SE_CheckBoxIndicator, btn, widget); drawPrimitive(PE_IndicatorCheckBox, &subopt, p, widget); subopt.rect = subElementRect(SE_CheckBoxContents, btn, widget); drawControl(CE_CheckBoxLabel, &subopt, p, widget); if (btn->state & State_HasFocus) { QStyleOptionFocusRect fropt; fropt.QStyleOption::operator=(*btn); fropt.rect = subElementRect(SE_CheckBoxFocusRect, btn, widget); drawPrimitive(PE_FrameFocusRect, &fropt, p, widget); } Как можно видеть из фрагмента кода, общий стиль получает ограничивающие прямоугольники двух субэлементов и затем отрисовывает их. Если флажок в фокусе, то рамка фокуса также отрисовывается. Стиль java отрисовывает CE_CheckBoxIndicator, пока QCommonStyle обрабатывает CE_CheckboxLabel. Мы рассмотрим все реализации, а начнем с CE_CheckBoxLabel: const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt) uint alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter); if (!styleHint(SH_UnderlineShortcut, btn, widget)) alignment |= Qt::TextHideMnemonic; QPixmap pix; QRect textRect = btn->rect; if (!btn->icon.isNull()) { pix = btn->icon.pixmap(btn->iconSize, btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled); drawItemPixmap(p, btn->rect, alignment, pix); if (btn->direction == Qt::RightToLeft) textRect.setRight(textRect.right() - btn->iconSize.width() - 4); else textRect.setLeft(textRect.left() + btn->iconSize.width() + 4); } if (!btn->text.isEmpty()){ drawItemText(p, textRect, alignment | Qt::TextShowMnemonic, btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText); } visualAlignment() корректирует выравнивание текста согласно направлению компоновки. Затем отрисовываем пиктограмму, если есть, и корректируем промежуток слева от текста. drawItemText() отрисовывает текст, принимая выравнивание, направление компоновки и мнемонику в account. Также используется палитра для отрисовки текста правильным цветом. При отрисовке меток часто получается что-то запутанное. К счастью, обычно это обрабатывается базовым классом. Стиль java реализует собственную кнопку нажатия метки с тех пор противоположный Java к центрированной в окне содержимому кнопки также когда кнопка имеет пиктограмму. Вы можете изучить эту реализацию, если необходимо реализовать заново отрисовку меток. Взглянем на java-реализацию CE_CheckBoxIndicator в drawControl(): case PE_IndicatorCheckBox: { painter->save(); drawButtonBackground(option, painter, true); if (option->state & State_Enabled && option->state & State_MouseOver && !(option->state & State_Sunken)) { painter->setPen(option->palette.color(QPalette::Button)); QRect rect = option->rect.adjusted(1, 1, -2, -2); painter->drawRect(rect); rect = rect.adjusted(1, 1, -1, -1); painter->drawRect(rect); } if (option->state & State_On) { QImage image(":/images/checkboxchecked.png"); painter->drawImage(option->rect.topLeft(), image); } painter->restore(); break; Сначала мы сохраним состояние рисователя (painter). Это не всегда необходимо, но в данном случае QWindowsStyle нужен рисователь в том же состояние, в каком он был когда был вызван PE_IndicatorCheckBox (Конечно, состояние можно также установить с помощью вызовов функций). Затем используем drawButtonBackground() для отрисовки фона индикатор флажка. Это вспомогательная функция, которая отрисовывает фон, а также рамку кнопок нажатия и флажков. Мы рассмотрим эту функцию ниже. Затем мы проверим, если мышь наведена на флажок. Если да, мы отрисовываем рамку флажка java, имеющуюся когда квадрат не нажат, а мышь наведена на него. You may note that java does not handle tristate boxes, so we have not implemented it (I should do that actually; I say myself that we style Qt's widgets and not Java's). Для нашего индикатора мы используем рисунок png. Также мы можем проверить здесь, если виджет отключен. Тогда мы используем другое изображение с индикатором в неактивных цветах. void JavaStyle::drawButtonBackground(const QStyleOption *option, QPainter *painter, bool isCheckbox) const { QBrush buttonBrush = option->palette.button(); bool sunken = option->state & State_Sunken; bool disabled = !(option->state & State_Enabled); bool on = option->state & State_On; if (!sunken && !disabled && (!on || isCheckbox)) buttonBrush = gradientBrush(option->rect); painter->fillRect(option->rect, buttonBrush); QRect rect = option->rect.adjusted(0, 0, -1, -1); if (disabled) painter->setPen(option->palette.color(QPalette::Disabled, QPalette::WindowText)); else painter->setPen(option->palette.color(QPalette::Mid)); painter->drawRect(rect); if (sunken && !disabled) { drawSunkenButtonShadow(painter, rect, option->palette.color(QPalette::Mid), option->direction == Qt::RightToLeft); } } Мы увидели, как флажки стилизованные в стиле java от получения виджетом запроса на рисование и до окончания отрисовки стиля. Чтобы детально изучить как отрисовывается каждый виджет, вам нужно изучить код по шагам так же как сделали здесь мы. Однако, обычно достаточно знать какие элементы стиля отрисовывают виджет. Виджет создает опцию стиля и вызывает стиль один или более раз для отрисовки элементов стиля из которых он состоит. Обычно, этого также достаточно чтобы знать в каких состояниях может быть виджет и другое содержимое опции стиля, например, перечисленное в следующем разделе. Критический разбор виджетовВ этом разделе мы рассмотрим как стилизуются большинство виджетов Qt. Будем надеяться, это сэкономит вам немного времени и усилий во время разработки ваших стилей и виджетов. Вы не найдете здесь информации, которой нет еще где-нибудь (например, рассматривая исходный код или описания для связанных со стилем классов). В качестве примеров главным образом используются виджеты со стилем java. Стиль java не отрисовывает каждый элемент в деревьях элементов. Это потому, что они невидимы для этих виджетов в стиле java. Кроме того мы должны убедиться, что все элементы реализованы способом, согласованным со стилем java, так как пользовательским виджетам они могут понадобиться (хотя это не исключает предоставление реализаций QWindowsStyle). Нижеперечисленное предоставлено для каждого виджета:
Дерево элементов содержит простейшие, управления и сложные элементы управления стиля. Делая нисходящий обход дерева элементов, вы получите последовательность в которой элементы должны быть отрисованы. В узлах мы подписали прямоугольники субэлементов, субэлементов управления и пиксельные метрики, которые учитываются во время отрисовки элемента узла. Наш подход к стилевому решению сосредоточен на отрисовке виджетов. Вычисления прямоугольников субэлементов, субэлементов управления и пиксельных метрик используемые во время отрисовки только перечислены как содержимое деревьев элементов. Обратите внимание, что имеются прямоугольники и пиксельные метрики, которые используются только виджетами. Это оставляет эти вычисления за пределами критического разбора. Например, функции subControlRect() и sizeFromContents() часто вызывают subElementRect() для вычисления своих ограничивающих прямоугольников. Мы также можем нарисовать деревья для них. Однако, как выполняются эти вычисления вполне для отдельных стилей, и они не следуют особой структуре (Qt не навязывает особую структуру). Тем не менее, вы должны убедиться, что используете подходящие пиксельные метрики. Для ограничения размера документа, мы решили не включать деревья или описания вычислений, выполняемых стилем Java (или любых других). При рассмотрении деревьев вас может озадачить использование различных пиксельных метрик, прямоугольников субэлементов и прямоугольников субэлементов управления. Если вы сомневаетесь после прочтения описаний перечислений QStyle, мы советуем рассмотреть реализации QCommonStyle и QWindowsStyle. Некоторые из выделенных на изображениях виджетов ограничивающих прямоугольников одинаковы. Причины в том, что некоторые элементы отрисовывают фоны в то время, как другие отрисовывают фреймы и метки. Если сомневаетесь, проверьте описание каждого элемента в QStyle. Также некоторые элементы относятся к компоновке, т.е., решают где отрисовывать другие элементы. Общие свойства виджетаНекоторые состояния и переменные являются общими для всех виджетов. Они устанавливаются с помощью QStyleOption::init(). Не все элементы используют эту функцию; это виджеты, создающие опции стиля, а для некоторых элементов информация из init() не является необходимой. Таблица общих состояний:
Другими общими членами для виджетов являются:
Сложные опции стиля (классы, наследуемые от QStyleOptionComplex) используются для сложных элементов стиля разделяя две переменных: subControls и activeSubControls. Обе переменных являются объединением ИЛИ переменных из QStyle::SubControl. Они отображают из каких субэлементов управления состоит сложный элемент управления и какие из этих элементов управления сейчас активны. Как упоминалось, стиль вычисляет размеры содержимого виджета, из подсказки которых виджеты вычисляют свои размеры. Кроме того, сложные элементы управления также используют стиль для проверки над каким субэлементом управления находится мышь. Руководство по виджетамБез дальнейших задержек мы представляем критический обзор виджетов; каждому виджету отведен свой подраздел. Кнопки нажатияНиже показана структура стиля для кнопок нажатия. Делая нисходящий обход дерева, вы получите последовательность в которой элементы должны быть отрисованы. Компоновка кнопок, с относительными границами элемента, изменяется от стиля к стилю. Из-за чего показывать ее концептуальные изображения становится сложно. Также, элементы могут - даже были запланированы - иметь те же границы; PE_PushButtonBevel, например, используется в QCommonStyle для отрисовки элементов, которые его содержат: PE_FrameDefaultButton, PE_FrameButtonBevel и PE_PanelButtonCommand, которые все имеют одни и те же границы в обычном и windows стилях. PE_PushButtonBevel также отвечает за отрисовку индикатор меню (QCommonStyle отрисовывается PE_IndicatorArrowDown). Ниже приведено изображение кнопки нажатия в стиле java, на котором показаны ограничивающие прямоугольники элементов. Для ограничивающих прямоугольников на изображении используются разные цвета; другого назначения они не имеют. Это справедливо также для похожих изображений для других виджетов. Стиль java, так же как и другие реализованные в Qt стили, не использует PE_FrameButtonBevel. Обычно кнопка с PE_DefaultFrame корректирует прямоугольник PE_PanelButtonCommand с помощью PM_ButtonDefaultIndicator. CE_PushButtonLabel находят корректируя прямоугольник rect с помощью PM_DefaultFrameWidth. Теперь рассмотрим опцию стиля для кнопок нажатия - QStyleOptionButton. Таблица состояний, которые QPushButton может установить на опцию стиля, следующая:
Остальные члены QStyleOptionButton:
Флажки и радиокнопкиСтруктура зависимых переключателей и флажков одинакова. Покажем структуру, используя элемент QCheckBox и имена пиксельных метрик: QStyleOptionButton используется как опция стиля и для зависимых переключателей, и для флажков. Сначала представим таблицу состояний, которые можно установить в опции:
Таблицу с остальными членами QStyleOptionButtonClass смотрите в Push Buttons. ВкладкиВ Qt QTabBar используется для отрисовки своих вкладок стилем. Tabs exist either in a QTabWidget, which contains a QTabBar, or as a separate bar. If the bar is not part of a tab widget, it draws its own base. QTabBar lays out the tabs, so the style does not have control over tab placement. However, while laying out its tabs, the bar asks the style for PM_TabBarTabHSpace and PM_TabBarTabVSpace, which is extra width and height over the minimum size of the tab bar tab label (icon and text). The style can also further influence the tab size before it is laid out, as the tab bar asks for CT_TabBarTab. The bounding rectangle of the bar is decided by the tab widget when it is part of the widget (still considering CT_TabBarTab). The tab bar is responsible for drawing the buttons that appear on the tab bar when all tabs do not fit. Their placement is not controlled by the style, but the buttons are QToolButtons and are therefore drawn by the style. Here is the style structure for QTabWidget and QTabBar: The dotted lines indicate that the QTabWidget contains a tab bar, but does not draw it itself, that QTabBar only draws its base line when not part of a tab widget, and that the tab bar keeps two tool buttons that scroll the bar when all tabs do not fit; see Tool Buttons for their element tree. Also note that since the buttons are children of the tab bar, they are drawn after the bar. The tabs bounding rectangles overlap the base by PM_TabBarBaseOverlap. Here is a tab widget in the java style: In the java style (and also windows), the tab bar shape and label have the same bounding rectangle as CE_TabBarTab. Notice that the tabs overlap with the tab widget frame. The base of the tab bar (if drawn) is the area where the tabs and frame overlap. The style option for tabs (QStyleOptionTab) contains the necessary information for drawing tabs. The option contains the position of the tab in the tab bar, the position of the selected tab, the shape of the tab, the text, and icon. After Qt 4.1 the option should be cast to a QStyleOptionTabV2, which also contains the icons size. As the java style tabs don't overlap, we also present an image of a tab widget in the windows style. Note that if you want the tabs to overlap horizontally, you do that when drawing the tabs in CE_TabBarTabShape; the tabs bounding rectangles will not be altered by the tab bar. The tabs are drawn from left to right in a north tab bar shape, top to bottom in an east tab bar shape, etc. The selected tab is drawn last, so that it is easy to draw it over the other tabs (if it is to be bigger). A table of the states a tab bar can set on its tabs follows:
Обращаем внимание, что отдельные вкладки могут быть отключены даже если панель вкладок включена. Вкладка включена, если включена панель вкладок. Вот таблица членов QStyleOptionTabV2:
The frame for tab widgets use QStyleOptionTabWidgetFrame as style option. Здесь мы перечислим его члены. It does not have states set besides the common flags.
Полосы прокруткиHere is the style structure for scrollBars: QScrollBar simply creates its style option and then draws CC_ScrollBar. Some styles draw the background of add page and sub page with PE_PanelButtonBevel and also use indicator arrows to draw the arrows in the nest and previous line indicators; we have not included these in the tree as their use is up to the individual style. The style's PM_MaximumDragDistance is the maximum distance in pixels the mouse can move from the bounds of the scroll bar and still move the handle. Here is an image of a scrollbar in the java style: You may notice that the scrollbar is slightly different from Java's as it has two line up indicators. We have done this to show how that you can have two separate bounding rectangles for a single sub control. The scroll bar is an example of a widget that is entirely implemented by the java style - neither QWindowsStyle nor QCommonStyle are involved in the drawing. We have a look at the different states a scroll bar can set on the style option:
The style option of QScrollBar is QStyleOptionSlider. Its members are listed in the following table. The option is used by all QAbstractSliders; we only describe the members relevant for scroll bars here.
ПолзункиWhen calculating the sliders size hint, PM_SliderTickness and PM_SliderLength is queried from the style. As with scroll bars, the QSlider only lets the user move the handle if the mouse is within PM_MaximumDragDistance from the slider bounds. When it draws itself it creates the style option and calls drawComplexControl() with CC_Slider: Мы также показываем изображение ползунка в стиле java. We show the bounding rectangles of the sub elements as all drawing is done in CC_Slider. QSlider uses QStyleOptionSlider as all QAbstractSliders do. We present a table with the members that affect QSlider:
You should note that the slider does not use direction for reverse layouts; it uses upsideDown. Окошки счетчикаWhen QSpinBox paints itself it creates a QStyleOptionSpinBox and asks the style to draw CC_SpinBox. The edit field is a line edit that is a child of the spin box. The dimensions of the field is calculated by the style with SC_SpinBoxEditField. Here follows the style tree for spin boxes. It is not required that a style uses the button panel primitive to paint the indicator backgrounds. You can see an image below the tree showing the sub elements in QSpinBox in the java style. The QStyleOptionSpinBox, which is the style option for spin boxes. It can set the following states on the spin box.:
The rest of the members in the spin boxes style options are:
Строка заголовкаThe title bar complex control, CC_TitleBar, is used to draw the title bars of internal windows in QMdiArea. It typically consists of a window title and close, minimize, system menu, and maximize buttons. Some styles also provide buttons for shading the window, and a button for context sensitive help. The bar is drawn in CC_TitleBar without using any sub elements. How the individual styles draw their buttons is individual, but there are standard pixmaps for the buttons that the style should provide. In an image over a title bar in the java style, we show the bounding rectangles of the sub elements supported by the java style (all of which are drawn with standard pixmaps). It is usual to draw the button backgrounds using PE_PanelButtonTool, but it's no rule. The style option for title bars is QStyleOptionTitleBar. It's members are:
Комбинированный список выбораA QComboBox uses the style to draw the button and label of non-editable boxes with CC_ComboBox and CE_ComboBoxLabel. The list that pops up when the user clicks on the combo box is drawn by a delegate, which we do not cover in this overview. You can, however, use the style to control the list's size and position with the sub element SC_ComboBoxListBoxPopup. The style also decides where the edit field for editable boxes should be with SC_ComboBoxEditField; the field itself is a QLineEdit that is a child of the combo box. We show an image over a java style combo box in which we have outlined its sub elements and sub element rectangles: Java combo boxes do not use the focus rect; it changes its background color when it has focus. The SC_ComboBoxEdit field is used both by QComboBox to calculate the size of the edit field and the style for calculating the size of the combo box label. The style option for combo boxes is QStyleOptionComboBox. It can set the following states:
The style options other members are:
Групповые рамкиWhen calculating the size hint, QGroupBox fetches three pixel metrics from the style: PM_IndicatorWidth, PM_CheckBoxLabelSpacing, and PM_IndicatorHeight. QGroupBox has the following style element tree: Qt does not impose restrictions on how the check box is drawn; the java style draws it with CE_IndicatorCheckBox. See Check and Radio Buttons for the complete tree. We also give an image of the widget with the sub controls and sub control rectangles drawn: The style option for group boxes are QStyleOptionGroupBox. The following states can be set on it:
Остальные члены QStyleOptionGroupBox:
РазделителиAs the structure of splitters are simple and do not contain any sub elements, we do not include image of splitters. CE_Splitter does not use any other elements or metrics. For its style option, Splitters uses the base class QStyleOption. It can set the following state flags on it:
QSplitter does not use init() to set up its option; it sets the State_MouseOver and State_Disabled flags itself. Индикатор выполненияThe CE_ProgressBar element is used by QProgressBar, and it is the only element used by this widget. We start with looking at the style structure: Here is a progress bar in the windows style (the java style bounding rectangles are equal): The style option for QProgressBar is QStyleOptionProgressBarV2. The bar does not set any state flags, but the other members of the option are:
Инструментальные кнопкиTool buttons exist either independently or as part of tool bars. They are drawn equally either way. The QToolButton draws only one style element: CC_ToolButton. As you must be used to by now (at least if you have read this document sequentially), we have a tree of the widget's style structure: Note that PE_FrameButtonTool and PE_IndicatorArrowDown are included in the tree as the java style draws them, but they can safely be omitted if you prefer it. The structure may also be different. QWindowsStyle, for instance, draws both PE_IndicatorButtonDropDown and PE_IndicatorArrowDown in CE_ToolButton. We also have an image of a tool button where we have outlined the sub element bounding rectangles and sub controls. Here is the states table for tool buttons:
QStyleOptionToolButton also contains the following members:
Панели инструментов (Toolbars)Toolbars are part of the main window framework and cooperates with the QMainWindow to which it belongs while it builds its style option. A main window has 4 areas that toolbars can be placed in. They are positioned next to the four sides of the window (i.e., north, south, west, and east). Within each area there can be more than one line of toolbars; a line consists of toolbars with equal orientation (vertical or horizontal) placed next to each other. QToolbars in Qt consists of three elements CE_ToolBar, PE_IndicatorToolBarHandle, and PE_IndicatorToolBarSeparator. It is QMainWindowLayout that calculates the bounding rectangles (i.e., position and size of the toolbars and their contents. The main window also uses the sizeHint() of the items in the toolbars when calculating the size of the bars. Вот дерево элементов для QToolBar: The dotted lines indicate that the QToolBar keeps an instance of QToolBarLayout and that QToolBarSeparators are kept by QToolBarLayout. When the toolbar is floating (i.e., has its own window) the PE_FrameMenu element is drawn, else QToolbar draws CE_ToolBar. Вот изображение панели инструментов в стиле java: QToolBarSaparator uses QStyleOption for their style option. It sets the State_horizontal flag if the toolbar they live in is horizontal. Other than that, they use init(). The style option for QToolBar is QStyleOptionToolBar. The only state flag set (besides the common flags) is State_Horizontal if the bar is horizontal (i.e., in the north or south toolbar area). The member variables of the style option are:
МенюМеню в Qt реализованы в QMenu. The QMenu keeps a list of action, which it draws as menu items. When QMenu receives paint events ,it calculates the size of each menu item and draws them individually with CE_MenuItem. (Menu items do not have a separate element for their label (contents), so all drawing is done in CE_MenuItem. The menu also draws the frame of the menu with PE_FrameMenu. It also draws CE_MenuScroller if the style supports scrolling. CE_MenuTearOff is drawn if the menu is to large for its bounding rectangle. In the style structure tree, we also include QMenu as it also does styling related work. The bounding rectangles of menu items are calculated for the menus size hint and when the menu is displayed or resized. The CE_MenuScroller and CE_MenuTearOff elements are handled by QCommonStyle and are not shown unless the menu is to large to fit on the screen. PE_FrameMenu is only drawn for popup menus. QMenu calculates rectangles based on its actions and calls CE_MenuItem and CE_MenuScroller if the style supports that. It is also usual to use PE_IndicatorCheckBox (instead of using PE_IndicatorMenuCheckMark) and PE_IndicatorRadioButton for drawing checkable menu items; we have not included them in the style tree as this is optional and varies from style to style. The style option for menu items is QStyleOptionMenuItem. The following tables describe its state flags and other members.
The setup of the style option for CE_MenuTearOff and CE_MenuScroller also uses QStyleOptionMenuItem; they only set the menuRect variable in addition to the common settings with QStyleOption's init(). Горизонтальная панель меню (Menu Bar)QMenuBar uses the style to draw each menu bar item and the empty area of the menu bar. The pull-down menus themselves are QMenus (see Menus). The style element tree for the menu bar follows: The panel and empty area is drawn after the menu items. The QPainter that the QMenuBar sends to the style has the bounding rectangles of the items clipped out (i.e., clip region), so you don't need to worry about drawing over the items. The pixel metrics in QMenuBar is used when the bounding rectangles of the menu bar items are calculated. QStyleOptionMenuItem is used for menu bar items. The members that are used by QMenuBar is described in the following table:
QStyleOptionMenuItem is also used for drawing CE_EmptyMenuBarArea. QStyleOptionFrame is used for drawing the panel frame The lineWidth is set to PM_MenuBarPanelWidth. The midLineWidth is currently always set to 0. Заголовки отображений элементовIt is the style that draws the headers of Qt's item views. The item views keeps the dimensions on individual sections. Also note that the delegates may use the style to paint decorations and frames around items. QItemDelegate, for instance, draws PE_FrameFocusRect and PE_IndicatorViewItemCheck. Here is a QTableWidget showing the bounding rects of a Java header: The QHeaderView uses CT_HeaderSection, PM_HeaderMargin and PM_HeaderGripMargin for size and hit test calculations. The PM_HeaderMarkSize is currently not used by Qt. QTableView draws the button in the top-left corner (i.e., the area where the vertical and horizontal headers intersect) as a CE_Header. The style option for header views is QStyleOptionHeader. The view paints one header section at a time, so the data is for the section being drawn. Its contents are:
Индикаторы ветвей дереваThe branch indicators in a tree view is drawn by the style with PE_IndicatorBranch. We think of indicators here as the indicators that describe the relationship of the nodes in the tree. The generic QStyleOption is sent to the style for drawing this elements. The various branch types are described by states. Since there are no specific style option, we simply present the states table:
The tree view (and tree widget) use the style to draw the branches (or nodes if you will) of the tree. QStyleOption is used as the style for PE_IndicatorBranch has state flags set depending on what type of branch it is. Since there is no tree structure for branch indicators, we only present an image of a tree in the java style. Each state is marked in the image with a rectangle in a specific color (i.e., these rectangles are not bounding rectangles). All combinations of states you must be aware of are represented in the image. Набор инструментов (Tool Boxes)PM_SmallIconSize for sizeHints. QToolBox is a container that keeps a collection of widgets. It has one tab for each widget and display one of them at a time. The tool box lays the components it displays (the tool box buttons and selected widget) in a QVBoxLayout. The style tree for tool boxes looks like this: We show an image of a tool box in the Plastique style: All elements have the same bounding rectangles in the Plastique as well as the other Qt built-in styles. The style option for tool boxes is QStyleOptionToolBox. It contains the text and icon of the tool box contents. The only state set by QToolBox is State_Sunken, which is set when the user presses a tab down with the mouse. The rest of the QStyleOptionToolBox members are:
Элемент изменения размеров (Size Grip)The size grip calculates its size hint with CT_SizeGrip. The pixel metric PM_SizeGripSize is currently unused by Qt. The element tree for and an image in the Plastique style of QSizeGrip follows: We show the size grip in a QMainWindow's bottom right corner. The size grip style option, QStyleOptionSizeGrip, have one member except the common members from QStyleOption:
Резиновые линии (Rubber Band)The QRubberBand's style tree consists of two nodes. We present an image of a Java style window being moved in a QMdiArea with a rubber band: The style option for rubber bands is QStyleOptionRubberBand. Its members are:
Присоединяемые виджетыWhen the dock widget lays out its contents it asks the style for these pixel metrics: PM_DockWidgetSeparatorExtent, PM_DockWidgetTitleBarButtonMargin, PM_DockWidgetFrameWidth, and PM_DockWidgetTitleMargin. It also calculates the bounding rectangles of the float and close buttons with SE_DockWidgetCloseButton and SE_DockWidgetFloatButton. The dotted lines indicate that the sender keeps instances of the recipient of the arrow (i.e., it is not a style element to draw). The dock widget only draws PE_frameDockWidget when it is detached from its main window (i.e., it is a top level window). If it is docked it draws the indicator dock widget resize handle. We show a dock widget in both docked and floating state in the plastique style: The style option is QStyleOptionDockWidget:
For the buttons, QStyleOptionButton is used (see Tool Buttons for content description). The dock widget resize handle has a plain QStyleOption.
|
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |