Управляющий элемент таблица для
ленивых
Как работают клетки с контролами?
Горизонтальная прокрутка однородных
столбцов.
Горизонтальная прокрутка
неоднородных столбцов
В статье описывается управляющий элемент таблица.
Документация по PalmOS
очень скупо объясняет как использовать таблицу. Более того, начинающего
разработчика часто вводят в заблуждения попытки провести аналогию с другими
аналогичными элементами. Статья рассказывает о том, что такое таблица и как
ее правильно использовать. Описанные способы использования отнюдь не
единственные, они просто близки к тому, как планировали использовать таблицу
разработчики PalmOS.
В тексте используются кальки с английского языка. Control, элемент управления, обозначается как “контрол”. Форма, диалог – это form. Термин таблица используется для обозначения table control или абстрактной структуры. Там где нужно различать табличное как элемент интерфейса будет использоваться слово контрол, а для структуры - массив. Custom переводится как кастом. Scroll переводится как прокрутка.
Таблица – это контрол, предназначенный для визуализации данных из двумерного массива. Идеология таблиц отличается от идеологии различных Grid Control, используемых на больших компьютерах. В чем же отличия?
Как и любой другой контрол таблицу нужно описать в файле ресурсов. Рассмотрим формат описания таблицы:
TABLE ID
<Id.n> AT (<Left.p> <Top.p> <Width.p>
<Height.p>) [ROWS <NumRows.n>] [COLUMNS
<NumCols.n>] [COLUMNWIDTHS <Col1Width.n> ... <ColNWidth.n>]
Видно, что в ресурсах описываются только самые необходимые параметры таблицы:
У строк и столбцов имеется свойство usable. ОС рисует только те ячейки, у которых и строки и столбцы usable. Необходимо при инициализации таблицы выставить для нужных свойств и столбцов это свойство.
Также у строк и столбцов есть свойство masked. Это свойство было введено для поддержки режима mask hide records, при котором private записи показываются заштрихованными прямоугольниками.
При описании свойств в скобках идет имя поля, функции для доступа по чтению и записи.
У клетки есть 4 свойства:
Смысл полей полностью зависит от стиля клетки.
Псевдо-свойствами клетки являются:
Стиль клетки (TableItemStyleType) определяет, что будет в ней выводиться. Данные, хранящиеся в клетке, интерпретируются по-разному в зависимости от ее стиля.
|
Стиль |
Описание |
Какие поля в
клетке используются |
|
|
В клетке рисуется чекбокс. |
|
|
|
Содержимое клетки определяется программой. Высота клетки 11 пикселей. |
Никакие. Клетка рисуется пользовательской функцией |
|
|
Нередактируемя дата в виде month/day или прочерк, если дата –1. После даты выводится восклицательный знак, если дата уже прошла. |
В поле
Первые 7 бит – год – 1904, следующие 4 бита – месяц и последние 5 бит – день. Всегда используется стандартный шрифт. |
|
|
Текст, который не нужно редактировать. |
|
|
|
Нередактируемое число |
Числа всегда показываются жирным шрифтом. |
|
|
Листбокс с попап-триггером. |
Списки показываются стандартным шрифтом. |
|
|
Содержимое клетки определяется программой. Высота клетки определяется высотой строки. Этот тип был добавлен в PalmOS 4. |
Никакие. Клетка рисуется пользовательской функцией |
|
|
Редактируемый текст. Максимальная длина текста – tableMaxTextItemSize (255) байт. |
Используется контрол редактирования для вывода поля.
Функция |
|
|
Редактируемый текст с иконкой дополнительного текста справа. Размеры иконки заданы константами tableNoteIndicatorWidth tableNoteIndicatorHeight. Максимальная длина текста – tableMaxTextItemSize (255) байт. |
Используется контрол редактирования для вывода поля.
Функция |
|
|
Не реализовано. |
|
|
|
Редактируемый текст со свободным пространством справа. Максимальная длина текста – tableMaxTextItemSize (255) байт. |
|
5. Функции TblGrabFocus/TblReleaseFocus используются для установки режима редактирования. TblGrabFocus рекомендуется вызывать в фиксированной последовательности: FrmSetFocus на таблицу, TblGrabFocus на клетки и FldGrabFocus на редактор.
Чтобы не создавать множество контролов, таблица использует создание “по необходимости”. Так, чекбокс создается для отрисовки элемента и в случае отслеживания клика по клетке с ним. После выполнения этих операций контрол удаляется.
При выборе клетки с полем редактирования создается контрол редактора (Field). К этому контролу можно получить непосредственный доступ.
Первый пример посвящен созданию простой таблицы, целиком помещающейся на экране. Пример написан максимально просто для демонстрации разнообразия стилей клеток.
В ресурсах таблица описывается просто и понятно. Также описывается список для соответствующего стиля клеток.
TableDemo.rcp:
LIST "foo"
"bar" "boz" ID MainDemoList AT (100 100 30 35) NONUSABLE
TABLE ID
MainDemoTable AT (0 15 160 130) ROWS 10 COLUMNS 5
COLUMNWIDTHS 30 30 30 30 30
TableDemo.c:
//…
// ширина свобоного поля справа у текстового поля
#define
NARROW_FIELD_SIZE 10
// функция инициализации. Обратите внимание, что она
должна вызываться до
// FrmDrawForm.
Стандартная болванка от CodeWarrior
вызывает ее после. Либо
// переместите функцию, либо вызовите TblDrawTable в концк MainFormInit.
static void
MainFormInit(FormType *frmP)
{
UInt16 rows, cols, i, j;
TablePtr tableP;
//
получить указатель на объект
tableP = GetObjectPtr(MainDemoTable);
// получить размеры таблицы
rows = TblGetNumberOfRows(tableP);
cols = TblGetNumberOfColumns(tableP);
// сделать видимыми все
строки
for(i = 0; i < rows; ++i){
TblSetRowUsable(tableP,
i, true);
}
//
сделать видимыми все столбцы
// установить расстояние
между клетками в 2 пикселя
for(j = 0; j < cols; ++j){
TblSetColumnUsable(tableP,
j, true);
TblSetColumnSpacing(tableP,
j, 2);
}
//
установить стиль заголовков в первой строке
// для
демонстрации реализуем заголовки стилем customTableItem
for(i =
0; i < cols; ++i){
TblSetItemStyle(tableP, 0, i, customTableItem);
TblSetCustomDrawProcedure(tableP,
i, drawProc);
}
//
установить стиль ячеек из остальных строк
for(i = 1; i < rows; ++i){
char
*ptr;
//
колонка номер 0 показывает стиль labelTableItem
// для каждой ячейки выделяется блок памяти
со строкой
// память нужно освобождать при закрытии
формы
TblSetItemStyle(tableP, i, 0, labelTableItem);
ptr
= MemPtrNew(15);
StrPrintF(ptr,
"row%d", i);
TblSetItemPtr(tableP,
i, 0, ptr);
//
колонка номер 1 реализует чекбоксы и попапы
if (i % 2 == 0){
TblSetItemStyle(tableP,
i, 1, checkboxTableItem);
TblSetItemInt(tableP,
i, 1, i%3);
}else{
ListType
*listP = GetObjectPtr(MainDemoList);
TblSetItemStyle(tableP,
i, 1, popupTriggerTableItem);
TblSetItemInt(tableP,
i, 1, 0);
TblSetItemPtr(tableP,
i, 1, listP);
}
//
колонка номер 2 показывет различные виды клеток с редактируемым
// текстом
{
Int16
style;
// стиль клетки определятется остатком от
деления номера
// строки на три
switch (i % 3){
case
0:
style = textTableItem;
break;
case
1:
style = textWithNoteTableItem;
break;
case
2:
style = narrowTextTableItem;
break;
}
// выставляем стиль
TblSetItemStyle(tableP,
i, 2, style);
// выставляем ширину правого края для
узкого поля
TblSetItemInt(tableP, i, 2, NARROW_FIELD_SIZE);
// задаем процедуры загрузки и сохранения
TblSetLoadDataProcedure(tableP, 2, loadProc);
TblSetSaveDataProcedure(tableP,
2, saveProc);
}
//
колонка номер 3 показывает форматирование дат
TblSetItemStyle(tableP, i, 3, dateTableItem);
{
DateType
dd;
// получить текущюю дату в нужном формате
DateSecondsToDate(TimGetSeconds(), &dd);
// получить даты в диапазоне от “4 дня
назад”
//
до после-после-завтра
//
диапазон демонстрирует форматирование
//
прошедших и последующих дат
DateDaysToDate(DateToDays(dd) - 4 + i, &dd);
// структура преобразуется в 16-битное
число
TblSetItemInt(tableP, i, 3, *(Int16 *)&dd);
}
//
в колонке номер 4 заносятся числовые значения
TblSetItemStyle(tableP, i, 4, numericTableItem);
TblSetItemInt(tableP,
i, 4, i);
}
}
// функция рисования пользовательских клеток
static void
drawProc(void *tableP, Int16 row, Int16 column,
RectangleType *bounds)
{
// наименования столбцов
static const char *(names[]) =
{ "label", "checkbox",
"text", "date", "numeric"};
//
получить имя текущего столбца
const char *p = names[column];
// получить
отцентрированную координату по вертикали
Int16 y = bounds->topLeft.y + (bounds->extent.y
- FntCharHeight()) / 2;
RectangleType r;
if (row != 0){
//
эта функция будет вызываться для narrowTextTableItem, где будет
//
отрисовываться правый фрагмент
//
нарисуем справа символ подсказки
FontID fnt = FntSetFont(symbolFont);
char
ch = symbolHelp;
WinDrawChars(&ch,
1,
bounds->topLeft.x + bounds->extent.x
- NARROW_FIELD_SIZE + 1, bounds->topLeft.y);
FntSetFont(fnt);
return;
}
// здесь
рисуются заголовки в прямоугольной рамке
WinDrawTruncChars(p, StrLen(p), bounds->topLeft.x +
2, y,
bounds->extent.x);
r = *bounds;
RctInsetRectangle(&r, 1);
WinDrawRectangleFrame(rectangleFrame,
&r);
}
// функция загрузки текстового поля
static Err
loadProc(void *tableP, Int16 row, Int16 column, Boolean editable,
MemHandle
* dataH, Int16 *dataOffset, Int16 *dataSize, FieldPtr fld){
// набор параметров
позволяет подсовывать не только блоки памяти из
// кучи, но и записи в
базах. Ограничимся простейшим случаем.
// просто выделим
фиксированный блок.
MemHandle h;
char *p;
const int size = 30;
h = MemHandleNew(size);
p = MemHandleLock(h);
StrIToA(p, row);
MemHandleUnlock(h);
*dataH = h;
*dataOffset = 0;
*dataSize =