![]() |
![]() |
![]() |
![]() |
Эта глава охватывает следующие разделы:
Когда Вы создаете приложение, то обычно используете стандартные виджеты, обеспеченные библиотекой виджетов Фотона. Но иногда у Вас будет причина для не использования этих стандартных виджетов.
Например, виджеты Фотона требуют изрядного количества памяти, так что если Вы рисуете диаграмму, имеющую несколько виджетов PtRect, PtLine и PtPolygon, может быть лучше создать единственный виджет, который может нарисовать всю диаграмму, используя графические примитивы Pg*().
Другая причина для создания заказного виджета это потребность в объекте пользовательского интерфейса, чьи свойства не поддерживаются стандартными виджетами. В этом случае у Вас есть три выбора:
Размещение заказного кода пользовательского интерфейса в виджете имеет свои преимущества:
Процесс создания виджета в Фотоне очень прямолинейный. Поскольку все виджеты следуют определенной структуре или формату, Вы можете начать со стандартного шаблона виджета или существующего виджета, который имеет подобные функциональные возможности. Много примеров и шаблонов виджетов находятся в директории /qnx4/phtk/src/widgets.
Создание заказного виджета также известно как подклассификация виджета, потому что виджеты всегда создаются как потомки существующего виджета суперкласса. Подклассификация позволяет Вам расширять или изменять поведение, связанное с существующим суперклассом виджета, если Вы понимаете, как суперкласс осуществлен. Глава Использование Суперклассов Виджетов описывает наиболее общие виджеты суперклассы.
При создании заказного виджета, Вы должны подклассифицировать его из PtBasic или одного из подклассов PtBasic. Суперкласс PtBasic наследственный класс большинства стандартных виджетов Фотона. Подклассификация виджета из PtBasic позволяет виджету наследовать свойства, общие для большинства виджетов Фотона, типа поддержки отслеживания клавиатуры, подсвечивания и фокуса. Если Вы создаете виджет, который не поддерживает эти общие возможности, он может выглядеть неуместным в приложении Фотона.
![]() |
Если Вы будете добавлять заказной виджет к палитре виджетов Разработчика Приложений Фотона (PhAB - Photon Application Builder), есть несколько специальных дезайнерских соображений, которые Вы должны знать. Прежде, чем Вы начьнете разрабатывать ваш виджет, прочитайте главу Связь Виджетов с PhAB. |
Часть создания заказного виджета включает решение, как виджет будет выглядеть и как пользователь взаимодействует с ним. Вы будете должны остановиться на ресурсах, которые управляют виджетом, и выбрать соответствующий виджет суперкласс, с которого начать.
При настройке атрибутов рассмотрите, как представить их в структуре образца виджета. Например, будет ли атрибут строкой, структурой, или указателем на распределенный массив? Каждый из этих атрибутов (читаются ли он, записывается, или читается/записывается) будет ресурсом вашего виджета.
Вы можете обрабатывать все события одной и тойже функцией, или можете определить функцию для каждого типа событий. Функции, которые обрабатывают события, должны читать данные события и принимать соответствующее действие. Например, щелчок на вашем виджете может изменить его внутреннее состояние. Если ваш виджет должен быть поврежден, так чтобы последующая операция перерисовки могла отразить новое состояние виджета.
Виджет обычно составляется из двух файлов:
Следующая диаграмма изображает файлы заголовка и исходного кода для простого примера виджета, ShadowedBox.
Файл заголовка и исходный файл для класса виджета.
Этот типовой виджет будет рисовать затененный блок, определять ресурс для изменения цвета тени, и определять ресурс для определения смещения тени.
Этот типовой виджет будет подклассифицироваться следующим образом:
PtWidget --> PtBasic --> ShadowedBox
ShadowedBox будет подклассом PtBasic и будет наследовать все ресурсы и поведение этого стандартного виджета Фотона.
Виджеты Фотона включают две структуры: структура класса и структура образца. Структура класса определена в исходном файле и обсуждена в следующем разделе. Структура образца содержится в файле заголовка, как показано ниже в отрывке из ShadowedBox.h файла заголовка:
/* ShadowedBox.h - файл заголовка виджета */ #include <Pt.h> /* ресурсы виджета */ #define SBW_SHADOW_COLOR Pt_RESOURCE( Pt_USER( 0 ), 0 ) #define SBW_SHADOW_OFFSET Pt_RESOURCE( Pt_USER( 0 ), 1 ) /* структура образца виджета */ typedef struct shadowed_box_widget{ PtBasicWidget_t basic; PgColor_t shadow_color; short shadow_offset; } ShadowedBoxWidget; /* указатель класса виджета */ extern PtWidgetClassRef_t *ShadowedBox;
Каждый образец виджета имеет свою собственную копию структуры образца, которая несет законченное состояние виджета, включая все, что может быть уникальным для этого образца. Виджеты определяют общедоступные декларации ресурсов, которые соответствуют членам структуры образца.
Первый член структуры образца должен быть структурой образца непосредственного суперкласса виджета. ShadowedBox это подкласс PtBasic, так что первый член его структуры образца имеет тип PtBasicWidget_t.
Когда класс виджета создан, членам структуры образца даны значения по умолчанию или могут быть установлены, используя декларации ресурсов. Члены структуры образца могут быть изменены вызовом PtSetResources() и прочитаны вызовом PtGetResources().
Файл заголовка также определяет внешнюю ссылку на указатель на класс виджета.
Организация исходного файла весьма проста. Сначала в нем определена структура класса. Имеется только одна копия структуры класса, которую используют все образцы виджета. Она инициализируется функцией создания класса, когда создается первый образец этого класса.
Члены структуры класса содержат указатели на методы и ресурсы, которые управляют тем, как библиотека Фотона обрабатывает образцы этого класса.
Следующая структура класса находится в исходном файле ShadowedBox.c:
/* ShadowedBox.c - исходный файл виджета */ #include "ShadowedBox.h" /* объявления прототипа */ PtWidgetClass_t *CreateShadowedBoxClass( void ); /* указатель на класс виджета - структура класса, функция создания */ PtWidgetClassRef_t WShadowedBox = { NULL, CreateShadowedBoxClass }; PtWidgetClassRef_t *ShadowedBox = &WShadowedBox; // // Функция значений по умолчанию ShadowedBox // static void shadowedbox_dflts( PtWidget_t *widget ) { ShadowedBoxWidget *sb = ( ShadowedBoxWidget * ) widget; PtBasicWidget_t *basic = ( PtBasicWidget_t * ) widget; basic->fill_color = Pg_WHITE; sb->shadow_color = Pg_BLACK; sb->shadow_offset = 4; } // // Функция рисования ShadowedBox // static void shadowedbox_draw( PtWidget_t *widget, PhTile_t *damage ) { ShadowedBoxWidget *sb = ( ShadowedBoxWidget * ) widget; PtBasicWidget_t *basic = ( PtBasicWidget_t * ) widget; PhRect_t shadow_rect, rect; PgColor_t color; // Мы хотим использовать функцию рисования PtBasic, чтобы получить рамки // и заданный по умолчанию фокус ..., но мы не хотим, чтобы она заполнила // фон цветом заполнения PtBasic, так что мы устанавливаем цвет заполнения // в прозрачный для функции рисования PtBasic. color = basic->fill_color; basic->fill_color = Pg_TRANSPARENT; PtSuperClassDraw( PtBasic, widget, damage ); // мы не хотим рисовать вне нашего холста! Так что мы отсекаем. PtCalcCanvas( widget, &rect ); PtClipAdd( widget, &rect ); basic->fill_color = color; shadow_rect = rect; shadow_rect.ul.x += sb->shadow_offset; shadow_rect.ul.y += sb->shadow_offset; PgSetStrokeColor( sb->shadow_color ); PgSetFillColor( sb->shadow_color ); PgDrawRect( &shadow_rect, Pg_DRAW_FILL_STROKE ); PgSetFillTransPat( basic->trans_pattern ); PgSetStrokeColor( basic->color ); PgSetFillColor( color ); rect.lr.x -= sb->shadow_offset; rect.lr.y -= sb->shadow_offset; PgDrawRect( &rect, Pg_DRAW_FILL_STROKE ); /* удаляем отсечение */ PtClipRemove(); } // // Функция создания класса ShadowedBox // PtWidgetClass_t *CreateShadowedBoxClass( void ) { // определим наши ресурсы static PtResourceRec_t resources[] = { SBW_SHADOW_COLOR, Pt_CHANGE_REDRAW, 0, Pt_ARG_IS_NUMBER( ShadowedBoxWidget, shadow_color ), 0, SBW_SHADOW_OFFSET, Pt_CHANGE_RESIZE_REDRAW, 0, Pt_ARG_IS_NUMBER( ShadowedBoxWidget, shadow_offset ), 0, }; // установим наши значения элемента класса static PtArg_t args[] = { { Pt_SET_VERSION, 110}, { Pt_SET_STATE_LEN, sizeof( ShadowedBoxWidget ) }, { Pt_SET_DFLTS_F, (long)shadowedbox_dflts }, { Pt_SET_DRAW_F, (long)shadowedbox_draw }, { Pt_SET_FLAGS, 0, Pt_RECTANGULAR }, { Pt_SET_NUM_RESOURCES, sizeof( resources ) / sizeof( resources[0] ) }, { Pt_SET_RESOURCES, (long)resources, sizeof( resources ) / sizeof( resources[0] ) }, }; // создадим класс виджета return( ShadowedBox->wclass = PtCreateWidgetClass( PtBasic, 0, sizeof( args )/sizeof( args[0] ), args ) ); }
В исходном файле выше, определение класса определяет функции shadowedbox_dflts() и shadowedbox_draw() для метода По Умолчанию и метода Рисования. Он также определяет, как обрабатываются ресурсы SBW_SHADOW_COLOR и SBW_SHADOW_OFFSET.
Как Вы можете видеть из этого примера, создание простого виджета не требует много кода. Вы можете взять этот пример виджета из каталога /qnx4/phtk/src/widgets и попробовать использовать его в вашем собственном приложении.
Давайте сделаем обзор фундаментальной структуры файла исходного кода виджета:
Раздел | Пример |
---|---|
Файлы заголовка | #include "ShadowedBox.h" |
Объявление указателя на класс | PtWidgetClassRef_t *ShadowedBox |
Методы функции | shadowedbox_dflts(), shadowedbox_draw() |
Функция создания класса | CreateShadowedBoxClass() |
Библиотека Фотона поставляет ряд классов виджетов, которые могут использоваться как основание для заказных виджетов. Чтобы облегчить понимание, мы разделили виджеты на три основных типа:
Все виджеты Фотона основаны на основном классе виджета PtWidget. Он определяет характеристики, общие для всех стандартных виджетов, типа позиции и размеров. Даже если ваш виджет отличается от любого другого виджета, он будет все еще наследовать свойства класса виджета PtWidget. Большенство заказных виджетов, однако, будут подклассифицироваться под одним из других виджетов суперклассов.
PtWidget также поддерживает свойства прикрепления, которые позволяют виджетам быть прикрепленными к их родителю.
Основные виджеты это объекты одной сущности. Они не содержат другие виджеты и не сформированы из других виджетов. Пример виджет кнопки.
Виджеты кнопки.
Вы не можете поместить другие виджеты внутрь кнопки, и сама кнопка не сформирована из других виджетов. Наш типовой виджет ShadowedBox это базовый виджет.
Хотя мы называем их "базовыми" виджетами, это только классификация и не подразумевает, что они не могут быть полнофункциональными. Фактически, один из наиболее сложных виджетов в библиотеке Фотона, PtText, является базовым виджетом.
Контейнерные виджеты могут иметь другие виджеты как дочерние. Контейнеры имеют опцию управления геометрией их дочерних виджетов (например PtGroup) or leaving the widgets as they are (например PtContainer).
Класс PtContainer расширяет определение класса PtBasic чтобы включить много новых методов для управления и реакции на дочерние виджеты виджета, помещенные внутри контейнера. Используя эти новые методы, контейнер может, например, обнаружить, когда добавлен новый дочерний виджет и автоматически изменить свои размеры, чтобы приспособить к новому виджету.
Дочерние виджеты виджета могут быть выборочно блокированы функцией дочерней системой переадресации. Это полезно, когда Вы хотите создать виджет, который принимает только определенные виджеты как дочерние (например PtMenuBar принимает только PtMenuButton виджеты как дочерние). Все другие виджеты переназначаются к родительскому виджету контейнера. Это заставляет их быть созданными на том же самом уровне, как и контейнер, вместо как дочерний виджет контейнера.
Контейнер с дочерними кнопками переключателями.
Составные виджеты сформированы из экспортируемых зависимых виджетов. Механизм экспорта позволяет доступ к основным виджетам, использующим их ресурсы и функции. Это уменьшает потребность дублировать каждый зависимый ресурс виджета в списке ресурса составного виджета.
Вы должны устанавливать ресурсы только для новых функциональных возможностей или для решения конфликтов, когда более чем один из того же самого класса виджета экспортировался. Механизм блокирования позволяет составным виджетам блокировать доступ к любому из зависимых ресурсов. Это позволяет Вам предотвращать любые действия, которые неблагоприятно затронули бы вид и поведение составного виджета.
Начиная с класса PtCompound потомка PtContainer, виджет наследует все функциональные возможности контейнерного виджета. Это означает, что составные виджеты могут поддерживать дочерние виджеты кроме тех, из которых они сформированы. Так как эта возможность обычно не желательна, Вы можете использовать функцию дочерней переадресации составного виджета, чтобы автоматически отклонить добавление нежелательных виджетов.
Хороший пример составного виджета PtComboBox -- он создан с использованием классов виджетов PtText и PtList и добавляет свои собственные функциональные возможности.
Составной виджет.
Другой хороший пример PtDivider -- он создан с использованием виджета PtGroup; полосы разделителя добавляются автоматически между виджетами в группе.
![]() |
![]() |
![]() |
![]() |
Все мессаги сюда:yanich@inbox.ru |