Управляющий элемент таблица для ленивых

Оглавление

Управляющий элемент таблица для ленивых.. 1

Оглавление. 1

Введение. 1

Терминология. 1

Что такое таблица?. 1

Описание таблицы в ресурсах.. 2

Свойство usable. 2

Свойство masked.. 2

Свойства клетки.. 2

Стили клеток. 3

Свойства столбца. 4

Свойства строки.. 5

Свойства таблицы... 5

Прочие функции.. 6

Как работают клетки с контролами?. 6

Пример простой таблицы... 7

Вертикальная прокрутка.. 9

Горизонтальная прокрутка.. 10

Горизонтальная прокрутка однородных столбцов. 10

Горизонтальная прокрутка неоднородных столбцов. 10

 

 

Введение.

В статье описывается управляющий элемент таблица. Документация по PalmOS очень скупо объясняет как использовать таблицу. Более того, начинающего разработчика часто вводят в заблуждения попытки провести аналогию с другими аналогичными элементами. Статья рассказывает о том, что такое таблица и как ее правильно использовать. Описанные способы использования отнюдь не единственные, они просто близки к тому, как планировали использовать таблицу разработчики PalmOS.

Терминология.

В тексте используются кальки с английского языка. Control, элемент управления, обозначается как “контрол”. Форма, диалог – это form. Термин таблица используется для обозначения table control или абстрактной структуры. Там где нужно различать табличное как элемент интерфейса будет использоваться слово контрол, а для структуры - массив. Custom переводится как кастом. Scroll переводится как прокрутка.

 

Что такое таблица?

Таблица – это контрол, предназначенный для визуализации данных из двумерного массива. Идеология таблиц отличается от идеологии различных Grid Control, используемых на больших компьютерах. В чем же отличия?

  1. Таблица реализовывалась не как абстрактный контрол, а как инструмент для стандартных приложений PalmOS. Поэтому не нужно удивляться тому, что при выводе клетки со стилем “метка” автоматически добавляется двоеточие, а шрифт всегда используется стандартный. В то же время наличие кастомного стиля позволяет реализовать совершенно произвольный вывод в клетку.
  2. Контрол разрабатывался с учетом того, что все клетки будут выводиться на экран. Бессмысленно заводить таблицу 100x100 клеток. Во-первых, будут проблемы с памятью, во-вторых, ее не удастся показать.
  3. Предполагается, что столбцы содержат однородную информацию. Например, пользовательская функция глобальна для столбца.
  4. Число строк и столбцов в таблице не меняется после ее создания.
  5. Если все свелось к матрице кастомных клеток, стоит подумать, а нужно ли вообще использовать таблицу.

 

Описание таблицы в ресурсах

Как и любой другой контрол таблицу нужно описать в файле ресурсов. Рассмотрим формат описания таблицы:

 

TABLE ID <Id.n> AT (<Left.p> <Top.p> <Width.p> <Height.p>) [ROWS <NumRows.n>] [COLUMNS <NumCols.n>] [COLUMNWIDTHS <Col1Width.n> ... <ColNWidth.n>]

 

Видно, что в ресурсах описываются только самые необходимые параметры таблицы:

  1. Идентификатор на форме
  2. Координаты границ таблицы в пикселах
  3. Число строк в таблице. Этот параметр нельзя изменять в процессе работы!
  4. Число столбцов в таблице. Этот параметр нельзя изменять в процессе работы!
  5. Ширина всех столбцов

 

Свойство usable

У строк и столбцов имеется свойство usable. ОС рисует только те ячейки, у которых и строки и столбцы usable. Необходимо при инициализации таблицы выставить для нужных свойств и столбцов это свойство.

 

Свойство masked

Также у строк и столбцов есть свойство masked. Это свойство было введено для поддержки режима mask hide records, при котором private записи показываются заштрихованными прямоугольниками.

Свойства клетки

При описании свойств в скобках идет имя поля, функции для доступа по чтению и записи.

 

У клетки есть 4 свойства:

  1. Стиль (itemType/TblSetItemStyle/)
  2. Шрифт (fontID/TblGetItemFont/ TblSetItemFont)
  3. 16-битное значение (intValue/TblGetItemInt/TblSetItemInt)
  4. Указатель на строку (ptr/TblGetItemPtr/TblSetItemPtr)

 

Смысл полей полностью зависит от стиля клетки.

 

Псевдо-свойствами клетки являются:

  1. Границы клетки TblGetItemBounds. Границы нигде не хранятся, а вычисляются исходя из свойств таблицы

 

Стили клеток

Стиль клетки (TableItemStyleType) определяет, что будет в ней выводиться. Данные, хранящиеся в клетке, интерпретируются по-разному в зависимости от ее стиля.

 

Стиль

Описание

Какие поля в клетке используются

CheckboxTableItem

В клетке рисуется чекбокс.

intValue  содержит состояние клетки

CustomTableItem

Содержимое клетки определяется программой. Высота клетки 11 пикселей.

Никакие. Клетка рисуется пользовательской функцией TableDrawItemFuncType, задаваемой для столбца. В полях intValue и ptr можно хранить свои данные.

DateTableItem

Нередактируемя дата в виде month/day или прочерк, если дата –1. После даты выводится восклицательный знак, если дата уже прошла.

В поле intValue хранится значение даты (тип DateType). Этот тип представляет собой 16-битное значение в формате:

yyyyyyymmmmddddd

Первые 7 бит – год – 1904, следующие 4 бита – месяц и последние 5 бит – день.

Всегда используется стандартный шрифт.

LabelTableItem

Текст, который не нужно редактировать.

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

NumericTableItem

Нередактируемое число

intValue

Числа всегда показываются жирным шрифтом.

popupTriggerTableItem

Листбокс с попап-триггером.

intValue

ptr

В intValue содержится индекс текущего значения в списке, а в ptr – указатель на список. Список можно получить, создав его невидимым на форме и работать с ним с помощью функций Lst*.

Списки показываются стандартным шрифтом.

tallCustomTableItem

Содержимое клетки определяется программой. Высота клетки определяется высотой строки. Этот тип был добавлен в PalmOS 4.

Никакие. Клетка рисуется пользовательской функцией TableDrawItemFuncType, задаваемой для столбца. В полях intValue и ptr можно хранить свои данные.

TextTableItem

Редактируемый текст. Максимальная длина текста – tableMaxTextItemSize (255) байт.

fontID

ptr

Используется контрол редактирования для вывода поля. Функция TableLoadDataFuncType используется для загрузки текста в поле, а функция TableSaveDataFuncType вызывается для сохранения значения.

textWithNoteTableItem

Редактируемый текст с иконкой дополнительного текста справа. Размеры иконки заданы константами tableNoteIndicatorWidth tableNoteIndicatorHeight. Максимальная длина текста – tableMaxTextItemSize (255) байт.

fontID

ptr

Используется контрол редактирования для вывода поля. Функция TableLoadDataFuncType используется для загрузки текста в поле, а функция TableSaveDataFuncType вызывается для сохранения значения.

TimeTableItem

Не реализовано.

 

narrowTextTableItem

Редактируемый текст со свободным пространством справа. Максимальная длина текста – tableMaxTextItemSize (255) байт.

fontID

ptr

intValue

В intValue содержится число пикселей свободной области справа. Используется контрол редактирования для вывода поля. Функция TableLoadDataFuncType используется для загрузки текста в поле, а функция TableSaveDataFuncType вызывается для сохранения значения. Свободное поле справа отрисовываются  пользовательской функцией TableDrawItemFuncType, задаваемой для столбца.

 

Свойства столбца.

  1. ширина в пикселях (width/TblGetColumnWidth/TblSetColumnWidth)
  2. признак маскированности (masked//TblSetColumnMasked). Клетка с маскированным столбцом и строкой рисуется серым.
  3. Признак выделения столбца. (editIndicator//TblSetColumnEditIndicator). При выборе клетки, в ее строке выделяются слева направо все клетки, у которых editIndicator = true. Клетки, находящиеся справа от клетки с editIndicator = false никогда не выделяются. По умолчанию все клетки устанавливаются в true, кроме текстовых.
  4. признак использования (usable//TblSetColumnUsable)
  5. Размер свободного пространства после клетки (spacing/TblGetColumnSpacing / TblSetColumnSpacing). По умолчанию установите tableDefaultColumnSpacing
  6. Функция для отрисовки кастомной клетки (drawCallback//TblSetCustomDrawProcedure)
  7. Функция для загрузки редактируемого текстового поля (loadDataCallback//TblSetLoadDataProcedure)
  8. Функция для сохранения редактируемого текстового поля (saveDataCallback//TblSetSaveDataProcedure)

 

Свойства строки

  1. 16-битный идентификатор (id/TblGetRowID /TblSetRowID). Идентификатор используется для хранения произвольной информации о строке. Также существует функция TblFindRowID для поиска строки с указанным идентификатором. Функция ищет только по usable строкам.
  2. Высота строки в пикселях (height/TblGetRowHeight/TblSetRowHeight).
  3. 32-битная информация о строке (data/TblGetRowData /TblSetRowData). Используется для хранения произвольной информации о строке. Также существует функция TblFindRowData  для поиска строки с указанным полем data. Функция ищет только по usable строкам.
  4. Признак использования (usable/TblRowUsable/TblSetRowUsable)
  1. Признак маскирования (masked/TblRowMasked/TblSetRowMasked).Клетка с маскированным столбцом и строкой рисуется серым.
  1. Признак грязных данных в строке (invalid/TblRowInvalid/TblMarkRowInvalid). При вызове TblRedrawTable будут перерисованы только строки с установленным признаком. Функция TblMarkTableInvalid устанавливает признак грязных данных у всех строк таблицы.
  2. Признак фиксированной высоты строки (staticHeight//TblSetRowStaticHeight). Признак необходимо установить, если нежелательно изменять высоту при вводе текста.
  3. Признак возможности выбора строки (selectable/TblRowSelectable /TblSetRowSelectable).

 

Свойства таблицы

 

  1. Идентификатор на форме (id//).
  2. Границы поля (bounds/TblGetBounds/ TblSetBounds)
  3. Признак визуализации (visible//TblDrawTable,TblEraseTable). Этот признак присутствует у всех контролов в PalmOS. XxxDrawyyy его выставляет, а XxxEraseyyy его снимает. При снятом признаке никаких операций с таблицей не производится.
  4. Признак возможности редактирования (editable//). Странный признак, при его выставлении TblHandleEvent игнорирует все сообщения.
  5. Признак наличия активного поля редактирования (editing/TblEditing/).
  6. Признак наличия выбранной клетки (selected/TblGetSelection/ TblSelectItem, TblUnhighlightSelection). Также существует полудокументированная функция для выбора клетки без отрисовки TblSetSelection.
  7. Приложение обеспечивает полосу прокрутки для поля редактирования (hasScrollBar//TblHasScrollBar). Обратите внимание, что поле указывает на признак для поля редактора, а не для таблицы!
  8. Признак использования (usable//)
  9. Число строк (numRows/TblGetNumberOfRows/)
  10. Число столбцов (numColumns/TblGetNumberOfColumns /)
  11. Текущая строка (currentRow/TblGetSelection / TblSelectItem, TblUnhighlightSelection) Также существует полудокументированная функция для выбора клетки без отрисовки TblSetSelection.
  12. Текущий столбец (currentColumn/TblGetSelection / TblSelectItem, TblUnhighlightSelection) Также существует полудокументированная функция для выбора клетки без отрисовки TblSetSelection.
  13. Верхняя показываемая строка (topRow/TblGetTopRow/). Похоже, что это рудимент попыток использовать контрол с вертикальной прокруткой. Поскольку функции для установки topRow не существует, то документированного способа показать таблицу не с первой строки просто не существует.
  14. Массив свойств столбцов (columnAttrs//)
  15. Массив свойств строк (rowAttrs//)
  16. Массив свойств клеток (items//). Клетки располагаются построчно.
  17. Активный контрол редактора (currentField/TblGetCurrentField /). Осмысленное значение возвращается только в режиме активного поля редактирования.

 

 

Прочие функции

  1. Для рисования контрола существуют три функции: TblDrawTable, TblRedrawTable, TblEraseTable. Первая рисует таблицу, вторая перерисовывает только грязные строки с признаком invalid, а третья очищает прямоугольник в координатах таблицы.
  2. Функция TblHandleEvent обрабатывает и генерирует сообщения, затрагивающие таблицу.
  3. Функция TblGetLastUsableRow возвращает последнюю используемую строку.
  4. Функции для вставки-удаления строк TblInsertRow и TblRemoveRow просто сдвигают и раздвигают строки без изменения их числа.

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 =