[Предыдущая] [Содержание] [Индекс] [Следующая]

Цикл Жизни Виджета

Эта глава охватывает следующие темы:

Эта глава исследует цикл жизни базового виджета, используя виджет 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()? Что делает библиотека виджетов? Давайте следовать процессу создание виджета и исследовать различные части кода виджета, как они выполняются.

Создание образца виджета

Первый шаг, создание образца виджета. Процесс создания образца виджета следующий:

  1. Класс виджета создан и инициализирован.
  2. Все члены структуры образца виджета инициализированы в значения по умолчанию.
  3. Применяются ресурсы, указанные через 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 ) );
}

Определение класса включает:

Массив ресурсов
Статический массив resources определяет ресурсы класса. К ресурсам класса обращаются через таблицу ресурсов, уникальных в классе ShadowedBox.
Массив класса виджета
Статический массив args определяет класс ShadowedBox. Он определяет методы класса ShadowedBox и его таблицу ресурсов.

Массив 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
Hosted by uCoz