Содержание

Как загружаются отображения

Открытие отображения в окне

Любое отображение открывается средствами платформонезависимого модуля Views. Процедура Views.Open обращается к хуку открытия, который скрыт в интерфейсе модуля, потому что является наследником Kernel.Hook и реализуется в эталонном каркасе в модуле StdDialog.

Особенности реализации Views.ViewHook в StdDialog

Интерфейс хука содержит всего три метода, которые дают возможность:

  1. сохранить отображение в файл,
  2. прочитать отображение из файла
  3. открыть отображение в окне (документе).

Мы рассмотрим последний метод. Он обращается к StdDialog.Open, который проделывает следующие вещи:

- Устанавливает внутренние флаги открытия окна в зависимости от комбинации входных параметров. Открытие может завершиться, если такой локатор уже открыт в каком-то окне. - Проверяет, является ли отображение документом Documents.Document. Именно это указывает на документоориентированность каркаса. Каркас открывает в окне отображение только документа. - Создает экземпляр документа, настраивает секвенсор и передаёт документ в фабрику Windows.Directory. Работу фабрики мы рассмотрим отдельно.

Модуль Windows

В документации каркаса есть раздел Compound Documents, в котором обоснована необходимость поддержки каркасом разделения на пространство документа и пространство отображения. Модуль StdDialog создаёт персистентный объект - документ, затем создаёт объект окна и передаёт их в модуль Windows, выводящий этот объект в пространство отображения.

В пространстве отображения существует абстракция фрейма, или устройства отображения. Отображение снабжается фреймом для того, чтобы получить возможность себя рисовать. Более подробно о фреймах см. раздел «Модули Views и Ports», а здесь мы поговорим о создании окон.

Директория Windows.Directory является абстрактным интерфейсом. В эталоне BlackBox эта абстракция реализуется модулем HostWindows. При обращении к интерфейсу Windows.Directory.New создаётся новый объект типа HostWindows.Window.

Открытие окна происходит посредством вызова HostWindows.Directory.Open, который расширяет метод Windows.Directory.Open и делает его супервызов. При открытии окна в модуле HostWindows создаётся экземпляр порта, которым инициализируется окно. Далее, отображение документа получает контекст окна, у документа запрашивается экземпляр корневого фрейма (а документ всегда возвращает корневой фрейм!), который затем подсоединяется к порту, и инициализируется отображением документа посредством Views.SetRoot.

После завершения супервызова с объектом окна работает модуль HostWindows, вызывая OpenDoc (или OpenDlg для инструментального окна). В этой процедуре средствами Windows создаётся окно, контроллер BlackBox-окна ставит фокус на отображение документа, Windows-окно так же получает фокус, а в конце OpenDoc вызывается WinApi-процедура обновления окна. На этом первая часть открытия окна средствами Windows заканчивается. В обработчик окна приходит сообщение WM_CREATE, на которое срабатывает вторая часть открытия окна CreateDoc.

Процедура CreateDoc средствами WinApi и самого каркаса настраивает окно, после чего сначала вызывает метод Windows.Window.Restore для перестройки дерева фреймов, а потом метод Windows.Window.Update для обновления фреймов на экране (см. «Построение дерева фреймов»).

Модули Views и Ports

Фрейм является т.н. «маппером» (mapper), или, другими словами, способом доступа к порту, на котором выводится отображение.

Порты это носители пиксельных данных, такие как экран или принтер. Доступ к порту осуществляется посредством бегунка (Ports.Rider), в координатной системе порта. Координаты фреймов преобразуются так, что они становятся независимыми от позиции фрейма в порте и от физического разрешения порта. Поэтому все операции во фреймах используют универсальные координаты, а все операции в бегунке порта используют пиксельные координаты.

Все операции ввода-вывода проходят через фрейм. Фрейм управляет раскладкой и обрезкой отображений на порте. Фреймы это непостоянные объекты, создаваемые каркасом по мере необходимости.

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

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

Построение дерева фреймов

Для построения и обновления дерева фреймов в модуле Views предусмотрены две процедуры UpdateRoot и ValidateRoot. Первая из них определяет видимые области, требующие перерисовки. Вторая сперва отбрасывает ненужные фреймы, а затем перестраивает заново дерево фреймов, вызывая RestoreRoot.

Внутри RestoreRoot вызывается рекурсивная процедура RestoreFrame, которая обходит дерево фреймов и для каждого привязанного к фрейму отображения вызывает метод v.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 в процессе перерисовки отображения (отображения могут вставлять внутрь себя другие отображения, при этом каркас создаёт новые фреймы).