winctl.f  Оконная библиотека: элементы управления


1. Общие свойства элементов управления
2. Строки статуса
3. Статические элементы
       Свойства
       Декоративные элементы
       Картинки
4. Кнопки
       Обычные кнопки
       Кнопки с отметкой
       Радиокнопки
5. Строки ввода
6. Списки
7. Комбинированные списки
8. Полосы прокрутки
9. Управление элементами
10. Сетки
       Описание размещения объектов
       Алгоритм размещения
11. Модальные диалоговые окна

1. Общие свойства элементов управления

Все элементы управления разделяют приведенные ниже свойства. Впрочем, для конкретного элемента не каждое свойство имеет смысл. Например, строки статуса не могут быть выравнены, поэтому -align к ним неприменимо.

-font

Шрифт надписей элемента. Шрифты создаются с помощью слова create-font.

-align

Выравнивание надписи в пределах элементах. Могут устанавливаться следующие константы: left - влево, center - по центру, right - вправо.

-command

Слово, которое будет выполнено при поступлении "главной" команды. Главная команда различается для каждого элемента. Для кнопок - это нажатие на кнопку, для строки статуса - щелчок по ней и т.д. По умолчанию NOOP.
Кстати будет заметить, что Windows очень болезненно относится к своему стеку. Поэтому во всех обработчиках типа -command, -painter и иже с ними, вызываемых из оконной процедуры, стек нужно обязательно оставлять в том же состоянии, в каком он был при вызове. Иначе ждите GPF, а то и полного зависания машины.

-notify

Список (MESSAGES:...MESSAGES;) обработчиков уведомлений. Уведомления зависят от конкретного элемента. При запуске обработчика lparam указывает на первое слово после стандартного NMHDR, поскольку там находится обычно самое интересное. Обратите внимание: в этих обработчиках НЕ нужно ставить флажок, обработан элемент или нет, в отличие от обработчиков -pre и -wndproc.

-updown

Некоторые элементы могут быть дополнены двумя стрелочками, так называемым спином. В этом свойстве хранится спин, если таковой имеется. Подробнее о спинах смотрите здесь.

-tooltip

Всплывающая подсказка для элемента. Устанавливается и читается точно так же, как и свойство -text. Подробнее о подсказках смотрите здесь.

-painter

Процедура нестандартной отрисовки элементов (для элементов с флагом customdraw). При вызове переменная windc указывает контекст рисования, paint-rect на границы рисования. Остальные параметры доступны через lparam.


2. Строки статуса

create-status ( win -- )

Добавить к окну строку статуса. Строка статуса при необходимости доступна через свойство -status окна.

split-status ( array no win -- )

Разделить строку статуса на no частей. В массиве array содержатся no ячеек - правые границы каждой части (в пикселях) или -1, показывающая, что ячейка продолжается до конца строки статуса.

set-status ( z no win -- )

Установить указанный текст в нужную часть статусной строки. Части нумеруются с нуля.


3. Статические элементы

Свойства

Помимо свойств, общих для всех элементов, у статических элементов добавляются следующие свойства:

-xpad, -ypad

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

-image

Картинка (для соответствующих элементов - bitmap, bitmap-button).

-state

Состояние элемента управления.

Декоративные элементы

rectangle ( -- ctl )

Создать прямоугольник.

hline ( -- ctl )

Создать горизонтальную линию.

filler ( -- ctl )

Создать невидимый элемент - заполнитель места.

label ( z -- ctl )

Создать надпись.

groupbox ( z -- ctl )

Создать рамку с надписью. См. также -boxed.

Картинки

icon ( hicon -- ctl )

Создать статическую иконку. Передается дескриптор иконки.

bitmap ( hbitmap -- ctl )

Создать статическую картинку.


4. Кнопки

Свойства кнопок такие же, как и свойства статических объектов.

Обычные кнопки

button ( z -- ctl )

Создает обычную нажимающуюся кнопку. При нажатии кнопки вызывается слово, хранящееся в -command. Остальные уведомления о состоянии кнопки приходят через -notify.
Фон и надписи обычных кнопок программно окрасить нельзя. Они могут быть только стандартного системного цвета.

icon-button ( hicon -- ctl )

Создает кнопку-иконку.

bitmap-button ( hbitmap -- ctl)

Создает кнопку-картинку.

-defbutton ( -- )

Объявляет последнюю созданную кнопку кнопкой по умолчанию. Такая кнопка выделяется визуально более толстой рамкой. Если нажать Enter, находясь в диалоговом окне, будет выполнена -command кнопки по умолчанию.

ok-button ( z xt -- ctl)

Вспомогательное слово: объявляет кнопку по умолчанию с надписью z и командой xt.

cancel-button ( z -- ctl)

Вспомогательное слово: объявляет кнопку "Отмена". При нажатии на нее модальный диалог будет снят с кодом IDCANCEL.

Кнопки с отметкой

checkbox ( z -- ctl )

Создает кнопку с отметкой. Состояние кнопки можно узнать (ctl -state@), установить (TRUE ctl -state!) или сбросить (FALSE ctl -state!).

Радиокнопки

radio ( value z -- )

Создать радиокнопку, входящую в последнюю объявленную группу. При выборе кнопки в эту группу будет записано значение value. Можно узнать текущее состояние кнопки (ctl -state@) или установить конкретную кнопку (TRUE ctl -state!).

GROUP ( ->bl; -- )

Объявить переменную, в которой будет храниться состояние группы. Узнать, какая из кнопок в группе установлена, можно, прочитав значение группы как обычную переменную. Если ни одна из кнопок не установлена, возвращается -1.

start-group ( group -- )

Все последующие радиокнопки будут относиться к указанной группе.

clear-group ( group -- )

Сбросить все кнопки в группе.

GROUP gg
gg start-group
  0 " Veni" radio  1 " Vidi" radio  2 " Vici" radio

gg @ \ установленная кнопка или -1

5. Строки ввода

edit ( -- ctl )

Создать однострочный элемент ввода. Высота элемента устанавливается по текущему шрифту.

limit-edit ( n ctl -- )

Ограничить поле ввода n символами.

multiedit ( -- ctl )

Создать многострочный элемент ввода.


6. Списки

Кроме общих для всех элементов свойств простые и комбинированные списки расширены следующим свойством:

-selected

Номер текущей выбранной строки. Строки нумеруются от нуля. Свойство можно читать и записывать.

listbox ( -- ctl)

Создает новый пустой список.

lb-addstring ( z ctl -- )

Добавляет в конец списка новую строку.

lb-insertstring ( z pos ctl -- )

Вставляет новую строку в указанную позицию списка.

lb-clear ( ctl -- )

Очищает список.

lb-count ( ctl -- n)

Возвращает количество элементов в списке.

lb-deletestring ( pos ctl -- )

Удаляет строку в указанной позиции.

fromlist ( adr pos ctl -- )

Заносит по адресу adr строку, хранящуюся в списке в указанной позиции.

lb-dir ( mask attr ctl -- )

Заполняет список именами файлов. Mask - маска файлов, attr - аттрибуты файлов.


7. Комбинированные списки

combo ( -- ctl)

Создает новый пустой комбинированный список.

addstring ( z ctl -- )

Добавляет в конец списка новую строку.

insertstring ( z pos ctl -- )

Вставляет новую строку в указанную позицию списка.

clear-combo ( -- )

Очищает список.

combo-count ( ctl -- n)

Возвращает количество элементов в списке.

deletestring ( pos ctl -- )

Удаляет строку в указанной позиции.

fromcombo ( adr pos ctl -- )

Заносит по адресу adr строку, хранящуюся в списке в указанной позиции.

combo-dir ( mask attr ctl -- )

Заполняет список именами файлов. Mask - маска файлов, attr - аттрибуты файлов.


8. Полосы прокрутки

-pos

Текущая позиция бегунка. Свойство можно читать и записывать.

-min

Минимальная позиция бегунка.

-max

Максимальная позиция бегунка.

hscroll ( -- ctl )

Создает горизонтальную полосу прокрутки. Не путайте этот элемент управления с полосой прокрутки окна. Сообщения от полосы прокрутки приходят на ее список обработчиков -notify.

vscroll ( -- ctl)

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


9. Управление элементами

this

Переменная, в которой хранится последний созданный элемент управления.

ctlresize ( w h ctl -- )

Меняет размер элемента управления.

ctlmove ( x y ctl -- )

Перемещает элемент управления в указанное место. Координаты отмеряются в пикселях от левого верхнего угла клиентской области родительского окна.

ctlshow ( ctl -- )

Показать элемент управления

ctlhide ( ctl -- )

Спрятать элемент управления.

place ( x y ctl -- )

Разместить элемент управления в указанном месте окна current-window. Это слово необходимо лишь в особых случаях - когда стандартное размещение с помощью сеток почему-либо не устраивает программиста.

remove ( ctl -- )

Удаляет элемент с родительского окна, но оставляет его в памяти. Слово, противоположное place.

Если нужно одновременно установить сразу несколько свойств, можно использовать операцию групповой установки. Понять ее работу проще всего на следующем примере:

" Пример" label  blue this -color!  white this -bgcolor!
эквивалентно
" Пример" label  (/ -color blue  -bgcolor white /)

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

edit (/ -size 120 20 /) |

А так определить имя элемента:

0 VALUE ec
...
edit (/ -name ec /) |

10. Сетки

Широко популярны два метода расстановки элементов управления по окну. Первый из них, предлагаемый компилятором ресурсов, заключается в том, что программист, расчертив предварительно на миллиметровке диалоговую панель, сам назначает каждому элементу координаты. Метод довольно скучный и неудобный. Второй метод - в духе графических сред - сводится к расстановке тех же панелей, но уже на экране, а координаты в файл ресурсов записывает сама машина. Этот метод удобней первого, но все-таки достаточно утомительный и, не побоюсь, этого слова, примитивный и отдающий глубоким бессловесным детством. В идеальном методе надо только сказать, что должно быть на экране, и сказать примерно, не вдаваясь в точные вычисления. Сетки есть попытка изобразить нечто в духе идеального метода. Собственно, это второе издание воображаемых таблиц из программы Blank, издание расширенное и дополненное согласно опыту эксплуатации и пожеланиям пользователей.

Представьте, что по всей поверхности окна раскинута невидимая сетка из горизонтальных рядов. Каждый ряд делится на произвольное число колонок. В каждой ячейке этой сетки живет один из элементов управления или другая, вложенная, сетка. Есть слова, которые задают различные параметры клетки сетки - поля, выравнивание элемента, ее ширину и т.д. При изменении размеров окна сетка соответствующим образом растягивается или сжимается, переставляя в нужные места своих обитателей. Существует определенный предел, дальше которого сетка уже не сжимается - когда все элементы управления занимают свои места плюс вокруг них стоят необходимые поля. По такому размеру сетки и выставляется первоначально размер окна. Если окно увеличивается, в ячейки сетки добавляется дополнительное пространство или же растягивается сам обитатель - в зависимости от пожеланий программиста. Некоторые обитатели могут занимать фиксированный размер, а их ячейки не будут растягиваться - это нужно при точном совмещении, например, заголовков или вкладок.

Сетки могут вкладываться друг в друга. Не забывайте вставлять вложенную сетку в объемлющую словом |.

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

Сетка окна хранится в его свойстве -grid. Подключение сетки к окну словом -grid! вызывает изменение размеров окна по размеру сетки.

Описание размещения объектов

GRID ( -- )

Начать новую сетку.

=== ( -- )

Разделитель рядов сетки.

| ( ctl/grid -- )

Добавить новую ячейку в текущий ряд и занести в нее элемент управления или вложенную сетку.

GRID; ( -- grid )

Закончить описание сетки.

Расположение и размеры обитателя ячейки описывается следующим образом:

-left, -center, -right ( -- )

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

-top, -middle, -bottom ( -- )

Выравнивать обитателя в пределах ячейки: по верху, по середине, по низу.

-xmargin, -ymargin ( n -- )

Установить горизонтальные или вертикальные поля в n пикселей каждое.

-xspan, -yspan ( -- )

При масштабировании сетки обитатель ячейки будет растянут в горизонтальном или вертикальном направлении так, чтобы занять всю ячейку за исключением полей.

-width, -height ( n -- )

Клетка будет занимать ровно n процентов от ширины или высоты всей сетки.

-xfixed ( -- )

Клетка фиксирована в горизонтальном направлении, и ее ширина не будет меняться при масштабировании.

-yfixed ( -- )

Текущий ряд зафиксирован и его высота не будет меняться при масштабировании.

-boxed ( -- )

Текущая сетка будет окружена обводкой, проходящей по середине полей. Имеет смысл для вложенных сеток.

-bevel ( -- )

Текущая сетка будет выглядеть вдавленной в родительское окно. Имеет смысл для вложенных сеток.

Алгоритм размещения

Расчет параметров cетки

  1. Находим ширину и высоту каждого обитателя клетки, прибавляем поля и получаем ширину и высоту каждой ячейки сетки.
  2. Находим ширину каждого ряда как сумму ширин всех его ячеек и высоту ряда как высоту самой высокой из ячеек.
  3. Находим ширину всей таблицы как ширину самого широкого ее ряда и высоту таблицы как сумму высот всех рядов.
  4. Для каждого ряда находим сумму всех фиксированных клеток этого ряда.
  5. Для таблицы находим общую высоту всех фиксированных рядов.
  6. Высчитываем ширину каждой клетки как относительную долю от ширины ряда минус фиксированные клетки ряда; высчитываем высоту каждого ряда как относительную долю от высоты таблицы минус сумма фиксированных рядов.

Отображение cетки на окно

  1. Если сумма ширин ячеек какого-либо ряда меньше общей ширины таблицы, добавляем в каждую ячейку (кроме фиксированных) дополнительное место пропорционально ширине ячейки.
  2. Пересчитываем относительные доли в точки по текущим размерам окна.
  3. Устанавливаем положение и размеры каждой ячейки и размещаем в ней ее обитателя.

11. Модальные диалоговые окна.

Создать немодальное диалоговое окно несложно. Собственно, для этого достаточно только применить уже описанные выше слова: создать окно (необязательно через dialog-window), присвоить ему сетку и, если нужно, чтобы работали диалоговые клавиши, занести TRUE в свойство -dialog. Для окон dialog-window последнее не требуется.

Сложнее с модальным окном, то есть окном, которое не дает переключиться на другие окна до своего завершения. Здесь уже требуется кое-какая дополнительная инфраструктура, заодно облегчающая пользование самим окном.

MODAL... ( ztitle -- )

Создает безымянное диалоговое окно с заголовком ztitle. Обращаться к такому окну можно через переменную dialog.

SHOW ( grid -- )

Присваивает сетку диалоговому окну и показывает его в центре главного окна программы. Ожидает, пока пользователь завершит работу диалога и скрывает диалоговое окно.

...MODAL ( -- )

Уничтожает безымянное диалоговое окно.

end-dialog ( code -- )

Устанавливает указанный код возврата и завершает диалог.

dialog-ok ( -- )

Завершает диалог с кодом IDOK (успешное завершение)

dialog-cancel ( -- )

Завершает диалог с кодом IDCANCEL (диалог отменен)

dialog-termination ( -- )

После выполнения команды SHOW из этой переменной можно узнать, как закончился диалог. В ней хранится IDOK, если пользователь нажал кнопку Enter и была установлена кнопка по умолчанию; IDCANCEL, если пользователь нажал Esc или закрыл диалог крестиком в углу. Программы могут устанавливать также собственные коды завершения диалога словом end-dialog.

PROC: defbutt
" Диалог завершился успешно" msg
dialog-ok
PROC;
\ ...

" Пример диалога" MODAL...
GRID
...
" Ok" defbutt ok-button |
\ ...

GRID; SHOW
\ Теперь самое время прочитать значения элементов управления диалога
...MODAL
Пример работы с элементами управления Пример: преобразование килограммов в фунты (автор: Е. Цымбалов)