![]() |
![]() |
![]() |
![]() |
Эта глава охватывает следующие темы:
Эта глава исследует цикл жизни базового виджета, используя виджет ShadowedBox как пример. Мы покажем Вам, как создавать, использовать и уничтожать этот виджет. Мы также очертим различия цикла жизни основного, контейнерного и составного виджетов.
Если виджет уже реализован или в процессе реализации, он автоматически прикрепляется библиотекой виджетов.
Прикрепление по умолчанию для контейнерного виджета.
PtAnchorWidget() и PtApplyAnchors() в библиотеке создания виджетов позволяют применять прикрепление в любое время, если родительский виджета имеет силу.
Если мы строим приложение и хотим использовать заказной виджет ShadowedBox ("базовый" виджет), мы просто создадим его вызвав PtCreateWidget():
PtWidget_t *box; PhArea_t area = { 10, 10, 100, 100 }; PtArg_t args[2]; PtSetArg( &args[0], Pt_ARG_AREA, &area, 0 ); PtSetArg( &args[1], SBW_SHADOW_COLOR, Pg_BLUE, 0 ); box = PtCreateWidget( ShadowedBox, Pt_DEFAULT_PARENT, 2, args ); PtRealizeWidget( box );
Что происходит, когда мы вызываем PtCreateWidget()? Что делает библиотека виджетов? Давайте следовать процессу создание виджета и исследовать различные части кода виджета, как они выполняются.
Первый шаг, создание образца виджета. Процесс создания образца виджета следующий:
Когда виджет создан через запрос к PtCreateWidget(), указанный класс виджета (то есть ShadowedBox) сначала проверяет инициализирован ли он. Так как виджет был создан, вызывается функция инициализации виджета, чтобы создать класс виджета.
В исходном файле ShadowedBox.c, класс виджета определен как структура ссылки класса:
PtWidgetClassRef_t WShadowedBox = { NULL, CreateShadowedBoxClass }; PtWidgetClassRef_t *ShadowedBox = &WShadowedBox;
Первый член это указатель на класс виджета, который инициализирован в NULL. Второй член это функция создания класса виджета. Если значение указателя на класс равно NULL, функция создания класса вызывается и устанавливает указатель на класс виджета в структуре.
Давайте посмотрим на CreateShadowedBoxClass() из нашего примера, чтобы увидеть, как она определяет класс ShadowedBox:
// // Функция создания класса 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 ) ); }
Определение класса включает:
Массив args передается к PtCreateWidgetClass() следующим образом:
return( ShadowedBox->wclass = PtCreateWidgetClass( PtBasic, 0, sizeof( args )/sizeof( args[0] ), args ) );
В PtCreateWidgetClass(), распределяется пространство для класса ShadowedBox и структура его суперкласса (то есть PtBasic) копируется в него. Затем используется массив args, чтобы установить заказной класс и его таблицу уникальных ресурсов.
После того, как класс ShadowedBox инициализирован, распределяется пространство для его структуры образца. Затем вызываются методы по умолчанию всех предков класса ShadowedBox, заканчивая методом по умолчанию ShadowedBox'а (то есть shadowedbox_dflts()):
// // Функция по умолчанию 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; }
Чтобы обратиться к частным членам структуры образца, этот метод по умолчанию приводит указатель widget к указателю ShadowedBoxWidget и указателю PtBasicWidget_t. Затем он устанавливает значения по умолчанию члена fill_color в Pg_WHITE, члена shadow_color в Pg_BLACK и члена shadow_offset в 4.
После того, как метод по умолчанию вызван, массив ресурсов, переданный к PtCreateWidget(), применяется. В типовом исходном коде, эти два ресурса переданы в args:
PtSetArg( &args[0], Pt_ARG_AREA, &area, 0 ); PtSetArg( &args[1], SBW_SHADOW_COLOR, Pg_BLUE, 0 );
Первая декларация ресурса, Pt_ARG_AREA, устанавливает область виджета (позиция и размер). Поскольку этот ресурс не определен классом ShadowedBox, он обрабатывается первым суперклассом, который определяет Pt_ARG_AREA (то есть PtWidget).
Вторая декларация ресурса SBW_SHADOW_COLOR обрабатывается классом ShadowedBox. Поскольку ShadowedBox не определяет метод для установки значения ресурса, библиотека виджетов Фотона заботится о назначении нового значения этому члену образца структуры (то есть значение изменено от значения по умолчанию, Pg_BLACK, в Pg_BLUE).
Наконец, PtCreateWidget() возвращают указатель на виджет, и приложение получает доступ к недавно созданному образцу класса ShadowedBox:
box = PtCreateWidget( ShadowedBox, Pt_DEFAULT_PARENT, 2, args );
Теперь box указывает на образец класса ShadowedBox, но сам виджет не будет отображен, пока он не реализован.
Есть ряд методов класса, используемых в процессе реализации виджета. Эти методы выполняются в определенном порядке, который мы назовем "шагами":
Шаг | Что он делает |
---|---|
Метод инициализации | Инициализирует образец виджета. |
Метод размерности | Вычисляет фактические размеры виджета, основанные на указанной области. |
Метод подключения | Создает любые необязательные области Фотона. |
Метод реализации | Выполняет любые операции, требуемые непосредственно перед рисованием. |
Метод рисования | Рисует виджет. |
Пример ShadowedBox не определяет метод Инициализации, метод Размеров, или метод Подключения. Эти методы обрабатываются PtWidget или PtBasic.
Прежде, чем мы начнем детальное обсуждение отдельных методов, используемых в классе виджета, мы должны взглянуть на их использование в общих чертах. Методы класса могут быть подключины вверх или вниз или наследованы.
Когда метод подключен, выполняется метод каждого класса в иерархии виджетов. Выполнение может начинаться с самого низшего класса (например ShadowedBox) в иерархии и идти вверх, или с самого высшего класса (то есть PtWidget) и идти вниз.
Методы, подключаемые вверх.
Некоторые цепочечные методы могут быть остановлены. Это означает, что любой класс в иерархии может останавливать последовательный процесс, чтобы предотвратить методы суперкласса или подкласса от выполнения. Когда формирование цепочки остановлено, класс, останавливающий цепочку, ответствен за поставку эквивалентных функциональных возможностей, иначе обеспеченных другим классом.
Наследуемые методы работают по-другому: вызывается только один метод, как определено наинизшим классом в иерархии (например наш класс ShadowedBoxне определяет метод Extent, так что этот метод наследуется от суперкласса PtBasic).
Наследуемые методы.
Это означает, что метод Extent PtBasic используется, чтобы определить размер виджета ShadowedBox. Наследуемые методы делают создание класса виджета проще, потому что должны быть определены только методы, уникальные к классу.
Давайте посмотрим на методы класса более подробно. Следующая таблица показывает, подключены методы базового виджета или унаследованы:
Метод | Обработка | Примечание обработки |
---|---|---|
Значения по умолчанию | Подключен вниз | Не может быть остановлен |
Инициализация | Подключен вверх | Возвращает Pt_END для остановки |
Размерности | Наследован | |
Подключение | Подключен вверх | Возвращает Pt_END для остановки |
Реализация | Наследован | |
Рисование | Наследован | |
Дереализация | Подключен вверх | Не может быть остановлен |
Разрушение | Подключен вверх | Не может быть остановлен |
Установка ресурсов | Наследован | |
Получение ресурсов | Наследован |
PtBasic расширяет эти методы тремя другими методами. Эти методы доступны только подклассам PtBasic:
Метод | Обработка |
---|---|
Получение Фокуса | Наследован |
Потеря Фокуса | Наследован |
Подсчет Непрозрачного Прямоугольника | Наследован |
Это первый метод класса, вызываемый в течение процесса реализации. Этот метод подключен вверх; метод Инициализации класса ShadowedBox вызывается первым, следующий методом Инициализации класса PtBasic, последним методом Инициализации PtWidget.
Этот унаследованный метод используется, чтобы определить точный размер виджета, основываясь на значениях по умолчанию и/или позиции виджета, размере, полях, рамках и подсвеченной информации, как установлено в программе. Метод Размерностей не определен для ShadowedBox, так что он унаследован от суперкласса PtBasic.
Метод Подключения подключен вверх. Он используется прежде всего для создания любых областей Фотона, необходимых для виджета. Из-за его простоты, виджет ShadowedBox не требует любых областей -- метод суперкласса создаст области для ShadowedBox только если необходимо (например определен заказной курсор).
Метод Реализации используется, чтобы скорректировать любые атрибуты области до фактического рисования виджета. Поскольку ShadowedBox не определяет метод Реализации, он наследуется от суперкласса.
Это последний метод класса, вызываемый в течение процесса реализации. Он используется, чтобы выполнить виджет на экране. Этот метод может быть наследован, но так как ShadowedBox определяет свой собственный метод Рисования (показанный ниже), он выполняется вместо метода Рисования суперкласса:
// // Функция рисования 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; // Мы хотим использовать функцию рисования базового виджета, // чтобы получить рамки и заданный по умолчанию фокус ..., но // мы не хотим заполнять фон цветом заполнения базоваого виджета, // так что мы устанавливаем цвет заполнения в прозрачный для функции // рисования базового виджета. 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(); }
Давайте посмотрим на код более подробно. От определения функции:
static void shadowedbox_draw( PtWidget_t *widget, PhTile_t *damage )
Методу Рисования передается два параметра. Параметр widget это указатель на виджет и параметр damage список прямоугольников ущерба, которые нужно применить к виджету, когда он рисуется.
![]() |
Программа может использовать список ущерба, чтобы делать интеллектуальные решения о том, что рисовать. Однако, операции рисования для большинства виджетов минимальны, и любая обработка дополнительного пространства может не заслуживать внимания. |
Обратите внимание, что указатель на виджет имеет тип PtWidget_t, и это означает, что указатель должен быть приведен к указателю на ShadowedBoxWidget чтобы позволить доступ к внутренним членам структуры виджета ShadowedBox. PtBasic это суперкласс ShadowedBox, мы можем привести указатель на виджет к структуре PtBasicWidget_t тоже; это позволяет доступ к членам базового виджета без необходимости прибегнуть к sb->basic.
ShadowedBoxWidget *sb = ( ShadowedBoxWidget * ) widget; PtBasicWidget_t *basic = ( PtBasicWidget_t * ) widget;
Так как метод Рисования не подключен, все рисование виджета ShadowedBox, включая рамки и подсвечивания общие для всех виджетов стиля Фотона, должно быть сделано в методе Рисования класса ShadowedBox. Для удобства, библиотека создания виджетов Фотона позволяет Вам вызывать метод рисования суперкласса, используя PtSuperClassDraw(), как показано ниже:
// Мы хотим использовать функцию рисования базового виджета, // чтобы получить рамки и заданный по умолчанию фокус ..., но // мы не хотим заполнять фон цветом заполнения базоваого виджета, // так что мы устанавливаем цвет заполнения в прозрачный для функции // рисования базового виджета. color = basic->fill_color; basic->fill_color = Pg_TRANSPARENT; PtSuperClassDraw( PtBasic, widget, damage );
После этого мы добавляем отсечение, так что мы не будем рисовать вне холста виджета:
// Мы не хотим рисовать вне нашего холста! Так что мы отсекаем. PtCalcCanvas( widget, &rect ); PtClipAdd( widget, &rect );
Остальная часть кода в Методе рисования определяет позицию виджета и размер, основанный на его области и полях, и затем тянет{*рисует*} затененный блок, используя низкоуровневый графические функции Фотона Pg*(). Перед выходом из метода Рисования, отсечение, которое мы добавляли, удаляется вызовом PtClipRemove().
Когда буфер рисования сбрасывается позже, виджет появляется на экране.
После того, как виджет реализован, он должен быть готовов:
К счастью, обе из этих операций обрабатываются автоматически вследствии пути, которым определены классы виджетов -- список ресурсов определяет то, что должно быть сделано, если значение ресурса изменяется:
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, };
В исходном коде примера для ShadowedBox, оба ресурса определены с декларацией Pt*REDRAW, так что они автоматически отмечаются как поврежденные. Это вызывает перерисовку, если значение изменяется.
Когда виджет поврежден по любой причине, библиотека Фотона автоматически вызывает метод Рисования виджета, чтобы восстановить повреждение. Если любой из наследуемых ресурсов изменен, он обрабатывается согласно объявлению ресурса первого суперкласса, который определил этот ресурс.
Виджет должен обработать свое собственное разрушение. Этот процесс включает:
Цель этого метода состоит в том, чтобы отменить все последствия, сделанные в течение реализации. Метод дереализации ответствен за:
Этот метод подключен вверх. Если класс ShadowedBox не определил метод Дереализации, будет вызван метод Дереализации PtBasic, следующий методом Дереализации PtWidget.
Этот метод ответствен за освобождение любых системных ресурсов, используемых виджетом, но не упомянутых ресурсом виджета. Подобно методу Дереализации, он подключен вверх. Это позволяет всем классам в иерархии освобождать системные ресурсы в свою очередь.
Оставшиеся методы класса не описаны в этом разделе, но Вы должны теперь иметь понимание всего процесса. Глава Анатомия Виджета описывает эти методы подробно. Давайте теперь посмотрим на некоторые из дополнительных требований для контейнерного и составного классов.
Цикл жизни контейнерного виджета подобен таковому базового виджета. Главное отличие в том, что контейнерные виджеты должны поддержывать дочерние виджеты, включая следующие возможности:
Метод Размеров контейнерного класса очень важен, он должен осуществить его политику изменения размеров основываясь на его дочерних виджетах. Это означает, что контейнерный виджет должен определить свои размеры, основываясь на позициях и размерах его дочерних виджетах. По этой причине все дочерние виджеты изменяют размеры прежде, чем контейнерный виджет.
Контейнерный класс расширяет определение класса PtBasic рядом методов с дочерним ограничением. Эти методы позволяют контейнеру корректироваться автоматически согласно любым изменениям, сделанным в его дочерних виджетах. Эти методы с дочерним ограничением включают:
Контейнер динамически идентифицирует, какие дочерние ограничения должны быть обработаны, установкой и очищением битов в члене flags его образца структуры класса. Например:
cntr->flags |= Pt_CHILD_CREATED;
Любой метод с дочерним ограничением, который контейнер поддерживает, должен иметь его множество битов в cntr->flags или метод не будет вызван.
Заказной контейнерный класс может произвольно обеспечивать функцию дочерней переадресации, чтобы ограничить некоторые классы виджетов от добавления в контейнер. Эта функция позволяет контейнеру переадресовывать происхождение к другим виджетам внутри или вне контейнера непосредственно. Функция переадресации обычно определяется в функции создания класса. Например:
{ Pt_SET_CHILD_REDIRECT_F, (long)PtSampContainerRedirect },
Виджет PtMenuBar использует эту возможность, чтобы предотвратить некоторые типы виджетов от добавления в строку меню. Для получения дополнительной информации, смотрите "Анатомия контейнерного виджета" в главе Анатомия Виджета.
Составной класс используется, чтобы разработать виджеты, сделанные из других виджетов. Составной класс это подкласс контейнерного класса, так что он наследует функциональные возможности, описанные выше для контейнеров. Кроме того, составной класс добавляет механизм для экспорта зависимых виджетов. Это достигается расширением определения контейнерного класса, чтобы включить:
Механизм экспорта позволяет пользователям устанавливать и получать ресурсы зависимых виджетов, обрабатывая составной виджет, как будто он фактически является зависимым виджетом.
Хороший пример это виджет PtComboBox -- он создан из текстового виджета и виджета списка. Он также определяет свои собственное поведение, обеспечивая кнопку около текстового виджета для доступа к виджету списка.
Вы можете изменять зависимые виджеты внутри составного виджета, используя ресурсы, определенные для зависимых виджетов. Например, Вы можете изменять виджет PtText внутри виджета PtComboBox используя указатель на виджет PtCompound следующим образом:
PtArg_t args[1]; PtSetArg( &args[0], Pt_ARG_TEXT_STRING, "Зависимый Текст" 0); PtSetResources( указатель_на_мойcombobox_виджет, 1, args);
Это очень мощная возможность, которое существенно упрощает конструкцию составных виджетов, так как ресурсы не должны быть дублированы.
Однако, изменение ресурсов зависимых виджетов может уничтожить вид и поведение составного виджета. По этой причине, мы рекомендуем, чтобы Вы рассмотрели использование механизма блокировки в заказном составном классе. Механизм блокировки позволяет составному классу отвергать попытки установки определенных ресурсов в его зависимых виджетах. Составной класс может переназначать любой ресурс, определенный его суперклассами или его зависимыми виджетами.
Составной виджет идентифицирует виджеты, которые он намеревается экспортировать, в функции создания класса. Это делается устанавливая значения для числа подчиненных и списка подчиненных.
Каждый виджет, указанный в списке подчиненных, должен быть создан в методе По Умолчанию составного класса.
Для получения дополнительной информации, смотрите "Анатомия составного виджета" в главе Анатомия Виджета. Для примера, каталог /qnx4/phtk/src/widgets содержит исходный код составного виджета названного PtSampCompound.
![]() |
![]() |
![]() |
![]() |
Все мессаги сюда:yanich@inbox.ru |