Любое отображение открывается средствами платформо-независимого модуля Views
. Процедура Views.Open
обращается к хуку открытия, который скрыт в интерфейсе модуля, потому что является наследником Kernel.Hook
и реализуется в эталонном каркасе в модуле StdDialog
.
Интерфейс хука содержит всего три метода, которые дают возможность:
Мы рассмотрим последний метод. Он обращается к StdDialog.Open
, который проделывает следующие вещи:
Documents.Document
. Именно это указывает на документо-ориентированность каркаса. Каркас открывает в окне отображение только документа.Windows.Directory
. Работу фабрики мы рассмотрим отдельно.
В документации каркаса есть раздел Compound Documents, в котором обоснована необходимость поддержки каркасом разделения на пространство документа и пространство отображения. Модуль StdDialog
создаёт персистентный объект - документ, затем создаёт объект окна и передаёт их в модуль Windows
, выводящий этот объект в пространство отображения.
В пространстве отображения существует абстракция фрейма, или устройства отображения. Отображение снабжается фреймом для того, чтобы получить возможность себя рисовать. Более подробно о фреймах см. раздел «Модули Views и Ports», а здесь мы поговорим о создании окон.
Директория Windows.Directory
является абстрактным интерфейсом. В эталоне BlackBox эта абстракция реализуется модулем HostWindows
. При обращении к интерфейсу Windows.Directory.New
создаётся новый объект типа HostWindows.Window
.
Открытие окна происходит посредством вызова HostWindows.Directory.Open
, который расширяет метод Windows.Directory.Open
и делает его супервызов. При открытии окна в модуле HostWindows
создаётся экземпляр порта, которым инициализируется1) окно. Далее, отображение документа получает контекст окна, у документа запрашивается экземпляр корневого фрейма (а документ всегда возвращает корневой фрейм!), который затем подсоединяется к порту, и инициализируется отображением документа посредством Views.SetRoot
.
После завершения супервызова с объектом окна работает модуль HostWindows
, вызывая OpenDoc
(или OpenDlg
для инструментального окна). В этой процедуре средствами Windows
создаётся окно, контроллер BlackBox-окна ставит фокус на отображение документа, Windows-окно так же получает фокус, а в конце OpenDoc
вызывается WinApi-процедура обновления окна. На этом первая часть открытия окна средствами Windows
заканчивается. В обработчик окна приходит сообщение WM_CREATE, на которое срабатывает вторая часть открытия окна CreateDoc
.
Процедура CreateDoc
средствами WinApi и самого каркаса настраивает окно, после чего сначала вызывает метод Windows.Window.Restore
для перестройки дерева фреймов, а потом метод Windows.Window.Update
для обновления фреймов на экране (см. «Построение дерева фреймов»).
Фрейм является т.н. «маппером» (mapper), или, другими словами, способом доступа к порту, на котором выводится отображение.
Порты — это носители пиксельных данных, такие как экран или принтер. Доступ к порту осуществляется посредством бегунка (Ports.Rider
), в координатной системе порта. Координаты фреймов преобразуются так, что они становятся независимыми от позиции фрейма в порте и от физического разрешения порта. Поэтому все операции во фреймах используют универсальные координаты, а все операции в бегунке порта — используют пиксельные координаты.
Все операции ввода-вывода проходят через фрейм. Фрейм управляет раскладкой и обрезкой отображений на порте. Фреймы это непостоянные объекты, создаваемые каркасом по мере необходимости.
Каждое окно содержит дерево фреймов, соответствующее всем видимым в окне отображениям. Поскольку рисование вне границ отображения должно быть запрещено, фреймы обеспечивают необходимую обрезку. Управление деревом фреймов и обрезков прозрачно для программиста, т. е. программист может об этом не задумываться.
При построении дерева фреймов, отображению может понадобиться особый фрейм, который запрашивается методом Views.View.GetNewFrame
. Если отображение не вернёт никакого фрейма, то ему назначается стандартный фрейм.
Для построения и обновления дерева фреймов в модуле Views
предусмотрены две процедуры: UpdateRoot
и ValidateRoot
. Первая из них определяет видимые области, требующие перерисовки. Вторая сперва отбрасывает ненужные фреймы, а затем перестраивает дерево фреймов заново , вызывая RestoreRoot
.
Внутри RestoreRoot
вызывается рекурсивная процедура RestoreFrame
, которая обходит дерево фреймов и для каждого привязанного к фрейму отображения вызывает метод v.Restore
(View.Restore — ?). Именно в этом месте отображения производят операции вывода в пространство отображения.
Теперь самое интересное. Перед вызовом RestoreFrame
открывается буфер порта, а после - закрывается. Это необходимо для накопления выводимых отображениями данных в буфере, который затем одномоментно «выбрасывает» их на экран или другое устройство отображения.
Это фрейм, находящийся на вершине дерева фреймов. С него начинается построение дерева, поэтому для обозначения корня есть специальный тип Views.RootFrame
. Поскольку корневой фрейм нельзя встраивать посреди других фреймов, то в процедуре Views.InstallFrame
стоит проверка типа фрейма.
Перед тем, как построить дерево фреймов, требуется связать корневой фрейм с его отображением. Связывание производится процедурой Views.SetRoot
. Сигналы от окна пойдут именно в это отображение, и далее к другим видимым отображениям. Корневое отображение отличается от других тем, что его метод GetNewFrame возвращает фрейм корневого типа. Таким образом, невозможно спутать назначение этого отображения.
Фрейм является маппером порта, а мапперу, согласно шаблону Carrier-Rider-Mapper, требуются для работы бегунок (Rider
) и носитель (Carrier
). Бегунком здесь является Ports.Rider
, а носителем Ports.Port
. При подключении фрейма к порту методом ConnectTo
для фрейма создаётся экземпляр бегунка. Объект порта уже должен быть создан платформенными средствами (его нельзя создавать иначе, ведь порт является абстракцией устройства вывода), при этом порту назначаются единицы измерения на устройстве и его габариты (ширина и высота) в единицах измерения.
Конечно же, для корневого отображения должен быть создан контекст, умеющий определять размеры окна. Кроме этого, потребуется проинициализировать домен отображения. И можно строить дерево.
Дерево фреймов строится последовательным вызовом двух процедур:
Views.AdaptRoot Views.RestoreRoot
Здесь сначала производится адаптация всех фреймов, начиная с корневого, к прямоугольнику отсечения, каковым является окно. А затем запускается построение дерева фреймов, которое растёт при помощи вызовов Views.InstallFrame
в процессе перерисовки отображения (отображения могут вставлять внутрь себя другие отображения, при этом каркас создаёт новые фреймы).