Table of Contents
Field Control - это штатный контрол PalmOS для редактируемого поля. Как обычно, умение работать с аналогичными контролами в других ОС как помогают, так и мешают работе с field.
Field традиционно проще своих десктопных аналогов.
Общие замечания
Отдельные символы в поле задаются позициями. позиция - смещение символа в байтах от начала поля.
Можно задать диапазон. Диапазон задается позицией первого символа диапазона и позицией символа после диапазона. Если позиции начала и конца совпадают, то диапазон пустой.
Описание в ресурсах
Приведем описание поля в файле ресурсов. Описание отдельных параметров будет приведено позже в разделе атрибутов
FIELD ID <Id.n> AT (<Left.p> <Top.p> <Width.p> <Height.p>) [USABLE] [NONUSABLE] [DISABLED] [LEFTALIGN] [RIGHTALIGN] [[FONT|<FontId.n>]] [EDITABLE] [NONEDITABLE] [UNDERLINED] [SINGLELINE] [MULTIPLELINES] [DYNAMICSIZE] [[MAXCHARS|<MaxChars.n>]] [AUTOSHIFT] [NUMERIC] [HASSCROLLBAR]
Атрибуты
В этом разделе перечислены атрибуты поля. После описания обычно даются функции по получению и установке атрибута, а также имя атрибута в файле ресурсов.
Имя поля | Описание | Функции извлечения | Функции модификации | Параметр в ресурсах | |||||
id | идентификатор поля на форме | ID <Id.n> | |||||||
rect | стандартные координаты поля типа RectangleType | FldGetBounds | FldSetBounds | AT (<Left.p> <Top.p> <Width.p> <Height.p>) | |||||
fontID | шрифт, используемый в поле | FldGetFont | FldSetFont | FONT <FontId.n>) | |||||
text | указатель на текст, содержащийся в поле. текст завершается нулевым байтом. | FldGetTextPtr | FldSetTextPtr | ||||||
textHandle | хендл текстового блока | FldGetTextHandle | FldSetTextHandle FldSetText | ||||||
textLen | реальная длина текста, используемая блоком, StrLen(text). | FldGetTextLength | |||||||
textBlockSize | длина блока, выделенного для текста. По сути дела максимальная длина текста, которую поле будет использовать до перевыделения памяти. | FldGetTextAllocatedSize | FldSetTextAllocatedSize | ||||||
maxChars | максимальное допустимое число вводимых символов. Учтите, что для мультибайтных кодировок это не будет совпадать с максимальной длиной блока в байтах. Предельный размер поля - 32767 байт. | FldGetMaxChars | FldSetMaxChars | MAXCHARS <MaxChars.n> | |||||
selFirstPost, selLastPos | позиции выделенного текста. | FldGetSelection | FldSetSelection | ||||||
insert position | позиция ввода. реально в структуре FieldType не хранится, вместо него хранятся следующие два поля. | FldGetInsPtPosition | FldSetInsPtPosition (с перерисовкой) FldSetInsertionPoint (без перерисовки) | ||||||
insPtXPos | смещение в байтах позиции ввода от начала строки. А если текущая строка не видна, то абсолютная позиция. | ||||||||
insPtYPos | строка на экране, в которой находится позиция ввода. Если позиция невидима, то в поле хранится 0x8000 | ||||||||
maxVisibleLines | максимальное число видимых строк | FldSetMaxVisibleLines | |||||||
lines | информация разбиения текста по строкам. Поле указывает на массив информации о началах строк и числе символов в каждой. Число строк можно получить с помощью функции FldGetVisibleLines. |
Следующие поля входят в структуру FieldAttrType. Для работы с этими полями используются функции FldGetAttributes и FldSetAttributes
Имя поля | Описание | Функции извлечения | Функции модификации | Параметр в ресурсах | ||||||
usable | признак использования поля в форме | FldSetUsable | USABLE NONUSABLE | |||||||
visible | признак видимости поля. | FldDrawField FldEraseField | DISABLED | |||||||
editable | признак возможности редактирования поля пользователем | EDITABLE NONEDITABLE | ||||||||
singleLine | Признак однострочного или многострочного поля | SINGLELINE MULTIPLELINES | ||||||||
hasFocus | поле держит фокус. Если поле в фокусе, то в нем мерцает курсор | FldGrabFocus FldReleaseFocus | ||||||||
dynamicSize | Обманчивое имя признака. На самом деле, если флаг выставлен, то при изменении числа строк генерируется событие fldHeightChangedEvent. Приложение может отслеживать событие и изменять видимые границы поля. Если признак не выставлен, то событие не генерируется. Никакого автоматического изменения размера не происходит. Для однострочных полей смысла не имеет. | DYNAMICSIZE | ||||||||
insPtVisible | внутренний признак того, что строка с позицией ввода видна на экране. | |||||||||
dirty | признак “грязного” поля. Поле является грязным, когда изменения в содержимом не отображены на экране. | FldDirty | FldSetDirty | |||||||
underlined | Признак указывает на тип подчеркивания строк. PilRC позволяет только стандартный способ - grayUnderline. Программно можно задать noUnderline, solidUnderline, colorUnderline. | UNDERLINED | ||||||||
justification | задает выравнивание поля: leftAlign, rightAlign. centerAlign не поддерживается. | LEFTALIGN RIGHTALIGN | ||||||||
autoShift | если признак задан, то поддерживаются правила autoShift (автоматическое выставление заглавных букв в начале предложений). | AUTOSHIFT | ||||||||
hasScrollBar | Еще одно обманчивое имя. При выставлении поле начинает чаще посылать fldChangedEvent. Слово “чаще” нужно понимать как “поле чаще пересчитывает разбиение на строки”. | HASSCROLLBAR | ||||||||
numeric | К версии PalmOS 3.5 поле научилось воспринимать только цифры, разделитель тысяч и десятичную точку. | NUMERIC |
Работа с памятью
Первое, что следует усвоить при работе с полями - это стратегии выделения памяти для содержимого поля.
- Фиксированное поле. textHandle содержит NULL, а пользователь функциями FldGetTextPtr и FldSetTextPtr работает с содержимым поля (text). При освобождении контрола память не освобождается. Такая техника должна применяться только к read-only полям.
- Динамическое поле в куче. Эта стратегия подразумевает, что памятью управляет контрол. Память выделяется контролом по необходимости в виде чанка и перевыделяется при превышении объема чанка. Хэндл чанка хранится в textHandle, связанный с ним указатель в text. Атрибут textBlockSize указывает максимальный размер памяти, доступный для использования без перевыделения.
- Динамическое поле в storage. Эта стратегия предназначена для модификации текста в базе. Именно так реализованы заметки во всех стандартных приложениях. Функция FldSetText содержит дополнительный параметр offset, который указывает смещение строки в чанке. Подразумевается что offset первых байт записи содержат прочую информацию. В остальном способ использования очень похож на предыдущий.
Функция FldFreeMemory освобождает память, выделенную для поля. В случае фиксированного поля ничего не освобождается. Если поле хранит содержимое в куче, то все освобождение происходит автоматически. В третьем случае функция попытается освободить память связанную с записью, что приведет к краху. Чтобы его избежать следует отвязать запись от поля вызовом
FldSetTextHandle(fldP, NULL)
.
Таким образом, аккуратного программирования требует только третья стратегия. Две первые ведут себя ожидаемо и предсказуемо. Не рекомендуется смешивать стратегии работы без необходимости.
Также не стоит сохранять указатель на текст в двух последних стратегиях, поскольку указатель может измениться.
Функция FldCompactText уменьшает размер чанка до текущего размера поля.
Рисование поля
Обычная отрисовка поля происходит автоматически. Программировать перерисовку руками нужно только есть вы изменяете содержимое поля функциями FldSetTextPtr/FldSetText/FldSetTextHandle. В этом случае используйте следующие функции:
- FldDrawField - отрисовка поля
- FldEraseField - очистка области, занимаемой полем. Признак visible сбрасывается и дальнейшие автоматические перерисовки будут запрещены до следующего вызова FldDrawField.
- FldRecalculateField - пересчитывает структуру lines и опционально перерисовывает поле. После вызова функций FldSet* вызов этой функции необходим.
Сверяйтесь со справочником при использовании функций, модифицирующих поле. Некоторые функции обновляют поле, а некоторые этого не делают.
Операции над полем
Для непосредственной работы с полем существуют привычные функции: FldCopy, FldCut, FldPaste. Эти функции реализуют стандартные функции взаимодействия поля с клипбордом.
Функция FldUndo откатывает последние операции. Функции отката могут откатить до 100 действий, сделанных над текущим полем.
Существует удобная функция для вставки в текущую позицию (или замены выделенного текста) - FldInsert. Пользоваться этой функцией удобно, но нужно учесть, что она заместит выделение и запостит в очередь сообщений fldChangedEvent. Сотня вставок с помощью FldInsert просто вызовет переполнение очереди. В этом случае проще получить хэндл текста, модифицировать содержимое и установить новый хэндл.
Функция FldDelete удаляет символы из указанного диапазона.
Взаимодействие со скроллбаром
Взаимодействие со скролл баром должно быть реализовано вручную. К счастью технология взаимодействия достаточно простая.
Описание поля и скроллбара в ресурсах:
FIELD ID MainDescriptionField AT (0 20 151 122) EDITABLE MULTIPLELINES LEFTALIGN MAXCHARS 8192 HASSCROLLBAR SCROLLBAR ID MainDescriptionScl AT (152 20 7 122) VALUE 0 MIN 0 MAX 100 PAGESIZE 8
Для обновления скроллбара нужно получить текущую позицию текста в поле с помощью функции FldGetScrollValues. Функция для обновления позиции скроллбара:
static void UpdateScrollBar(void){ UInt16 scrollPos; UInt16 textHeight; UInt16 fieldHeight; Int16 maxValue; FieldPtr fld; ScrollBarPtr bar; fld = GetObjectPtr (MainDescriptionField); bar = GetObjectPtr (MainDescriptionScl); FldGetScrollValues (fld, &scrollPos, &textHeight, &fieldHeight); if (textHeight > fieldHeight) { // On occasion, such as after deleting a multi-line selection of text, // the display might be the last few lines of a field followed by some // blank lines. To keep the current position in place and allow the user // to "gracefully" scroll out of the blank area, the number of blank lines // visible needs to be added to max value. Otherwise the scroll position // may be greater than maxValue, get pinned to maxvalue in SclSetScrollBar // resulting in the scroll bar and the display being out of sync. maxValue = (textHeight - fieldHeight) + FldGetNumberOfBlankLines (fld); } else if (scrollPos) maxValue = scrollPos; else maxValue = 0; SclSetScrollBar (bar, scrollPos, 0, maxValue, fieldHeight-1); }
Для точного позиционирования поля по первому видимому байту используются функции FldGetScrollPosition и FldSetScrollPosition. Но для грубых позиционирований достаточно использовать FldScrollField.
Выяснить, скроллится ли поле в указанном направлении можно с помощью функции FldScrollable.
Функция для скролла поля по изменению скролл бара:
static void ViewScroll (Int16 linesToScroll, Boolean updateScrollbar) { UInt16 blankLines; FieldPtr fld; fld = GetObjectPtr (MainDescriptionField); blankLines = FldGetNumberOfBlankLines (fld); if (linesToScroll < 0) FldScrollField (fld, -linesToScroll, winUp); else if (linesToScroll > 0) FldScrollField (fld, linesToScroll, winDown); // If there were blank lines visible at the end of the field // then we need to update the scroll bar. if ((blankLines && (linesToScroll < 0)) || updateScrollbar){ UpdateScrollBar(); } }
Фрагмент обработчика событий формы:
case fldChangedEvent: UpdateScrollBar(); break; case sclRepeatEvent: ViewScroll (e->data.sclRepeat.newValue - e->data.sclRepeat.value, false); break;
Динамический размер поля
PalmOS позволяет отслеживать высоту поля и изменять его размеры на форме. Изменение размеров нужно писать самому, PalmOS позволяет только отделить изменение поля от перерасчета размеров. Для отслеживания необходимо:
- Добавить в описание ресурса признак DYNAMICSIZE.
- Добавить в обработчик событий формы обработку fldHeightChangedEvent. Это сообщение указывает на необходимость изменения размеров поля.
- После ручных изменений содержимого поля следует вызвать FldMakeFullyVisible. Функция пересчитает число строк и добавит в очередь соответствующее событие.
- Функция FldSendHeightChangeNotification позволяет задать размеры поля вручную.
Как отследить изменение поля?
К сожалению, событие fldChangedEvent посылается при всех визуальных изменениях поля, в том числе и просто скроллах. Не существует надежного механизма для изменения переменной, связанной с полем при выходе из поля. Можно посоветовать только изменять переменную на все fldChangedEvent.
Прочие функции
Оставшиеся функции нужны редко. В основном они вызываются другими модулями PalmOS.
- FldHandleEvent обрабатывает события, посылаемые полю.
- FldNewField добавляет новое поле на форму.
- FldSendChangeNotification вручную посылает полю оповещение о перерисовке.
- FldGetTextHeight возвращает высоту непустых видимых строк.
- FldGetNumberOfBlankLines возвращает число видимых пустых строк в конце поля.
Следующие функции полезны при “ручной” реализации статических полей, они позволяют разбить длинную текстовую строку на отдельные строки заданной ширины:
- FldCalcFieldHeight - рассчитывает сколько строк займет текст в прямоугольнике указанной ширины.
- FldWordWrap - рассчитывает число байт, умещающихся в одну строку указанной ширины.
Полезные ссылки
http://www.palmosters.com/back/palmstranges.shtm - на странице описаны недокументированные нюансы программирования в PalmOS, в том числе некоторые грабли, связанные с филдами.