RDM/2 The Russian Electronic Developer Magazine  
RDM/2 Русский электронный журнал разработчика  
ДомойОт редактораПишите намОбратная связьRU/2

Современный внешний вид для программ в OS/2 PM.

Cell Toolkit это небольшая библиотека для ускорения создания программ для OS/2 PM. К особенностям этой библиотеки стоит отнести следующее:

Зачем это все нужно? Прежде всего, эта библиотека не есть нечто всеобъемлющее. Поэтому в первую очередь она приспособлена для создания небольших программ. Кроме того, размеры в этом случае имеют немаловажное значение, из-за чего она, собственно, и была сделана на голом API без применения чего-то готового в качестве базы.

Ну а теперь по порядку. Основная идея лежащая в основе Cell Toolkit - то, что всякое окно можно разделить на два, либо по горизонтали, либо по вертикали. Затем к этой базовой идее были прицеплены шашечки в виде возможностей иметь заданые заранее пропорции частей окна, сплитбар, возможность фиксировать размер подокна, и т.д. Затем были добавлены тулбары, с возможностью парковки их на любую границу окна. Наконец, я написал несколько примеров, которые, я надеюсь, помогут разобраться с библиотекой.

Чего хотелось получить.

Как уже упоминалось выше, основная идея - всякое окно можно разделить на два.
Например так:
    +---------+
    |         |
    +---------+
    |         |
    +---------+
Или так:
    +----+----+
    |    |    |
    |    |    |
    |    |    |
    +----+----+
Если разрешить делить вложеные окна, то можно получить, например, вот такую конструкцию:
    +----+----+
    |    |    |
    +----+----+
    |         |
    +---------+
Так, например, выглядит читалка ньюсов в Netscape 2.02. А вот так выглядит PMMail 2.0:
    +---+-----+
    |   |     |
    |   +-----+
    |   |     |  Рис.1
    +---+-----+
Как не трудно догадаться это и есть те самые split view. Очень удобно для использования, но довольно редко реализуемое решение. Хотя на самом деле никаких особых сложностей нет. Заглянем вовнутрь же Cell Toolkit.

Внутрености Cell Toolkit.

Библиотека состоит из двух больших частей, довольно плотно переплетенных между собой. Первая - собственно поддержка split view, вторая - тулбары.

Для реализации split view был выбран следующий подход. Окно, которое должно содержать split view разбивается на ячейки, например для такого окна которое изображено на Рис.1 такая разбивка выглядит так:

        +---------+
        |         |  Главное окно
        |         |
        |         |
        +---------+
            / \
           /   \         +-----+  верхняя панель
      +---+     +---+  / |     |
      |   |     |   | /  +-----+
      |   |     |   | \  +-----+  нижняя панель
      |   |     |   |  \ |     |
      +---+     +---+    +-----+
      левая     правая
      панель    панель
Кроме того, между панелями находится сплитбар, который обеспечивает возможность пользователю изменять соотношение размеров панелей. Сами панели и есть теми целевыми окнами, в которых происходит реальная работа пользователя: редактируется текст, просматриваются списки и т.д. Исключение в данном случае представляет собой правая панель, поскольку она является фреймом для вложеного split view. Хорошо видно, что все окна можно разбить на два вида: фреймы и конечные окна.

Для реализации фреймов используется достаточно простая и хорошо зарекомендовавшая себя техника subclassing. То есть, берется какой-либо стандартный класс и ему подменяется обработчик сообщений в результате чего можно видоизменить поведение такого окна. И для фреймов, естественно ;-) наиболее подходящим кандидатом является Frame Window. Что нам от него нужно в первую очередь, так это правильно разместить вложеные окна. Для этого достаточно перехватить два сообщения:

WM_QUERYFRAMECTLCOUNT
WM_FORMATFRAME
Первое сообщение возвращает количество подчиненных окон, а второе непосредственно отвечает за укладку этих элементов по площади окна. Подробности можно найти в функции CellProc.

Остается решить вопрос со сплитбаром. Как оказалось, самое простое решение это регистрация соответствующего класса и при создании фрейма в качестве класса для клиентского окна указывать этот класс. Это заметно упрощает жизнь, поскольку манипуляциями с этим окном в основном озабочен старый обработчик Frame Window. Все, что нужно - это правильно установить размер сплитбара при обработке WM_FORMATFRAME, не забыть его прорисовать и при прохождении курсора мыши над сплитбаром не забыть поменять форму курсора. Есть еще одна вещь, за которую тоже отвечает сплитбар: изменение размеров панелей. Детали этих действий можно найти в функции CellClientProc.

Теперь немножко о тулбарах. Непосредственно тулбар - это тоже фрейм, в котором расположены кнопки. Кнопки это обычные Push Button. Чего-то необычного в них нет, кроме, пожалуй, "пузырьковой" подсказки. Впрочем это сделано достаточно традиционным способом (subclassing), а техника создания такой подсказки была в деталях описана в одном из выпусков EDM/2.

Для реализации возможности присоединения/отсоединения тулбара не пришлось проделывать каких-то необычных трюков. Оказалось, что достаточно поменять окно-родитель для тулбара и он из части основного окна превратится в независомое окно или наоборот, из независимого окна станет частью окна программы.

Для сборки библиотеки и примеров понадобятся:

Собственно, вся библиотека (пока) это один .H файл и один .C файл, а все перечисленое выше нужно для сборки примеров. Имеющийся в комплекте makefile готов к употреблению вместе с Watcom 10.x.

Сборка под другие компиляторы делается в два этапа:

Теперь короткое руководство по использованию:

Для создания окна необходимо подготовить структуру типа CellDef (см. cell.h):
typedef struct stCellDef
{
    LONG lType;
    PSZ  pszClass;
    PSZ  pszName;
    ULONG ulStyle;
    ULONG ulID;
    struct stCellDef* pPanel1;
    struct stCellDef* pPanel2;
    PFNWP pClassProc;
    PFNWP pClientClassProc;
    LONG  lSize;
} CellDef;
lType
задается тип и флажки отвечающие за различные возможности окна. указание флага CELL_WINDOW означает, что это будет ячейка с управляющим эламентом (контролом), в противном случае (при задании CELL_VSPLIT или CELL_HSPLIT) - это контейнер для ячеек. Другие флаги описаны ниже.

pszClass
задает класс окна, для стандартных контролов это обычные константы из серии WC_ (WC_BUTTON, WC_MLE, ...). Это могут быть и Ваши собственные классы окон. Этот параметр игнорируется для фреймов. В этом случае всегда создается WC_FRAME.

pszName
этот параметр становится Window Text создаваемого окна. Если быть совсем точным, то она передается без изменений одним из параметров WinCreateWindow. Об этом можно почитать в тулките.

ulID
идентификатор окна.

ulStyle
это, опять таки, флаги которые будут переданы в WinCreateWindow. С одной оговоркой: для контейнера ячеек это флаги из серии FCF_, задающие внешний вид и поведение рамки окна.

pPanel1, pPanel2
указатели на описание ячеек содержащихся в контейнере. Эти указатели будут проигнорированы, если указан флаг CELL_WINDOW.

pClassProc
это процедура, которая будет заменять стандартную процедуру окна. В этой процедуре можно получить указатель на стандартный обработчик событий окна, достав с помощью WinQueryWindowULong() указатель на структуру. Тип структуры отличается для контейнера ячеек и ячеек. Для ячеек это WindowCellCtlData, для окон - CellCtlData. Вторая структура предназначена для внутреннего использования и по окончанию обработки сообщения необходимо вызывать не стандартный обработчик (CellCtlData->pOldProc), а CellProc. В любом случае, если Вам понадобится залезать так глубоко, то Вам придется детальнее смотреть исходный текст cell.c.

pClientClassProc
это процедура, которая будет заменять стандартный обработчик сообщений клиентской части окна контейнера ячеек. Более детально смотрите исходные тексты cell.c, процедура CellClientProc.

lSize
задает точный размер одной из ячеек. Например: для разделенной по вертикали панели это будет горизонтальный размер одной из ячеек, в зависимости от того, какой флаг указан в lType. Для CELL_SIZE1 это будет размер левой ячейки, для CELL_SIZE2 - правой. Для разделенной по горизонтали панели это будет, соответственно, размер нижней и верхней частей.
Флаги из серии CELL_:

CELL_WINDOW Ячейка является окном (контролом)
CELL_VSPLIT Контейнер ячеек поделен по вертикали
CELL_HSPLIT Контейнер поделен по горизонтали
CELL_SPLITBAR Контейнер содержит splitbar
CELL_FIXED Размер нельзя изменять, даже при наличии splitbar
CELL_SIZE1 Точный размер указан для панели 1 (нижняя или левая)
CELL_SIZE2 Точный размер указан для панели 2 (верхняя или правая)
CELL_SPLIT10x90Флаги задающие начальное отношение размеров ячеек
CELL_SPLIT20x80
CELL_SPLIT30x70
CELL_SPLIT40x60
CELL_SPLIT50x50
CELL_SPLIT60x40
CELL_SPLIT70x30
CELL_SPLIT80x20
CELL_SPLIT90x10

Например, структура CellDef, заполненая таким образом:

CellDef lPanel =
{
    CELL_VSPLIT | CELL_SPLITBAR | CELL_SPLIT70x30,
    0,
    "",
    WS_VISIBLE,
    ID_LPANE,
    &uPanel,
    &dPanel
};
создает разделенную по вертикали панель, с пропоциями левой и правой частей 70 к 30 и с описаниями внутренностей панелей в соответствующих структурах.

Примеры таких структур Вы найдете в multibar.c. Один из интересных примеров использования этой техники приведен в nbsample.c.

Затем используя описанные таким образом окна их можно создавать с помощью функции CreateCell. Кроме указателя на структуру-описатель, ей передаются два хендла окон: родитель и владелец. (Для тех, кто еще не разобрался в разнице между ними: родитель - это то окно, которое формально владеет окном, то есть отвечает за его положение на экране, поскольку координаты создаваемого окна отсчитываются от родителя, за видимость и уничтожение. Владелец - это то окно, которое будет получать сообщения, точнее уведомления от данного окна. Например, кнопка, у которой родителем указан десктоп, а владельцем окно Вашей программы, будет располагаться на десктопе, но сообщения о ее нажатии будет получать процедура обработки сообщений окна в Вашей программе). Для главного окна обычно указывают родитилем HWND_DESKTOP, а владельцем 0. Детали создания ячеек можно посмотреть в cell.c, процедура CreateCell.

Немножко о создании тулбаров.

Для создания тулбара, необходимо заполнить структуру TbDef. Она существенно проще и содержит всего три поля:
typedef struct
{
    LONG  lType;   // Toolbar flags
    ULONG ulID;    // Toolbar window ID
    ULONG *tbItems;
} TbDef;
lType
флаги.
ulID
идентификатор тулбара.
tbItems
указатель на описание кнопок.
TB_BUBBLE      - Тулбар имеет пузырьковую подсказку
TB_VERTICAL    - Расположен вертикально
TB_FLOATING    - Не присоединен к окну.
TB_ATTACHED_LT - Присоединен слева
TB_ATTACHED_TP - -//-//-//-- сверху
TB_ATTACHED_RT - -//-//-//-- справа
TB_ATTACHED_BT - -//-//-//-- снизу
Описание кнопок - это массив ULONG-ов, каждый из которых задает ID кнопки, и завершается 0. Специальный ID TB_SEPARATOR задает разделитель между кнопками на панели. Для каждой кнопки в файле ресурсов должен быть припасен битмап с таким же ID (смотрите примеры программ). Для TB_SEPARATOR ресурс не нужен. Если для тулбара указан флаг TB_BUBBLE, то обязательно наличие в файле ресурсов ресурса типа STRINGTABLE, и соответствующими ID.

Тулбар создается с помощью вызова CreateToolbar и становится частью окна.

Пара хинтов:
неприсоединенный тулбар можно переключать вертикальный / горизонтальный путем двойного клика левой кнопкой мышки на "ручке".
пузырьковую подсказку можно включать/выключать (если она была предусмотрена) путем двойного клика правой кнопкой мышки на "ручке".

Несколько слов в завершение.

Очевидно, что я не несу никакой ответственности за сие чудо враждебной технологии, несмотря на мое авторство :) Ну вобщем, понятно.

Есть несколько (один) известный мне баг и одна недоработка. Известный баг состоит в потере некоторого количества тулбаров при уменьшении размеров окна, а также при минимизации/восстановлении. Буду рад если кто-либо сумеет раскопать причину этой досадной проблемы и сообщит мне.
Недоработка состоит в полном отсутствии статусной строки как отдельной сущности в библиотеке. Посмотрите на status.c и скажите, а нужно ли?

Да, пользоваться Cell Toolkit можно совершенно свободно, при условии, сохранения моего (С) и при условии, что Вы не забудете помянуть меня в credits Вашей программы.

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

e-mail: evsi@naverex.kiev.ua
FIDO : 2:463/114.69
Sergey I. Yevtushenko.

---
Интересные ссылки:

---

---
Комментариев к странице: 0 | Добавить комментарий
---
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов
(C) Russian Underground/2