Перейти к содержимому
SheetForge

Ядро авторинга — построение второй поверхности авторинга

Продвинутый уровень. Для авторов ассетов/инструментов, которые хотят построить собственный интерфейс авторинга (например, холст с графом узлов) поверх движка SheetForge. Командам, использующим Data Studio, эта страница не нужна.

Окно авторинга — это не движок. Data Studio — и работающее рядом с ним веб-приложение — являются потребителями независимого от окна ядра авторинга.

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

Прежде чем строить целую поверхность, проверьте, не покрывает ли эту потребность уже существующая точка расширения. Плагин может добавить команды, панели, значки и виджеты ячеек в поставляемое окно, вообще не владея собственным окном, — описав их как данные, так что они отрисовываются и в редакторе, и в браузере — см. Создание плагинов §4.16. Эта страница — для случая, когда вам нужен собственный холст.

Сборка-симулятор потребителя без IVT (SheetForge.Tests.Consumer, без доступа InternalsVisibleTo в Core или Editor) реализует виртуальную поверхность авторинга от начала до конца, используя только публичный API. Если бы ей понадобился internal-член, сборка не скомпилировалась бы (CS0122). Поэтому она служит исполняемой спецификацией поверхности, описанной ниже.

Движок из трёх объектов

┌─────────────────────┐     ┌──────────────────────────┐     ┌───────────────┐
│  AuthoringSession   │────▶│   AuthoringDispatcher    │────▶│ BaselineStore │
│  (staging state)    │     │   .Reflect()             │     │ (round-trip   │
│                     │     │   (the full cycle)       │     │  snapshots)   │
└─────────────────────┘     └────────────┬─────────────┘     └───────────────┘
                                         │ binds
                            ┌────────────▼─────────────┐
                            │ AuthoringDispatchCallbacks│
                            │ (view concerns — YOUR UI) │
                            └──────────────────────────┘

AuthoringSession — состояние подготовленных изменений

Обычный класс [Serializable] (намеренно не ScriptableObject). Храните его в поле [SerializeField] вашего EditorWindow, и вы бесплатно получаете нативные для Unity снимки Undo и переживание перезагрузки домена — тот же механизм, что стоит за Ctrl+Z в поставляемом окне.

Он владеет всем подготовленным состоянием:

  • правками ячеек (Edits), новыми строками (NewRows), операциями структуры (StructOps);
  • изменениями порядка по вкладкам (Reorders), переименованиями вкладок (TabRenames);
  • якорями baseline, изолированными правками.

Поверх этого состояния он предоставляет API для изменения/запроса:

  • SetStaged(...) — подготовить правку ячейки. Правки несут логический адрес (вкладка · RecordId · поле); физический порядковый номер строки — это производный кеш, заново разрешаемый непосредственно перед отражением.
  • ResolveBaselineEdits(provider) — перепривязать все правки к текущему baseline. Разрешимые правки продолжают действовать. Три неразрешимых случая (внешнее переименование / внешнее удаление / конфликт ключа) перемещаются в IsolatedEdits: исключаются из отражения, помечаются значком, никогда не отбрасываются молча и никогда не блокируют сессию.
  • Поверхность чтения baseline: TabNames, TryGetBaselineTable(tab, out SheetTable) — типизированный доступ к схеме (TypeToken, @desc, @overlap) без необходимости самостоятельно трогать парсер.
  • EffectiveStructOps() / PendingStructCount() — составленное, каноническое представление операций структуры.
  • Хуки перепривязки (RemapFieldName / RemapRecordId / RemapTab) поддерживают согласованность подготовленного состояния при переименованиях.
  • LastProjectionResult кеширует последнюю проекцию.

AuthoringDispatcher — оркестрация отражения

var dispatcher = new AuthoringDispatcher(session, callbacks, baselineStore);
dispatcher.Reflect();   // the entire cycle, one call

Reflect() выполняет весь цикл по порядку:

  • предварительную проверку
  • отражение для конкретного источника — точечная запись для локального, безопасная перезапись для Google, ваша собственная цель для пользовательских провайдеров
  • очистку сохранённого состояния
  • границу подтверждения ClearUndo
  • автоматический повторный импорт с отчётом

Также:

  • BuildProjectionResult() — проекция текущего подготовленного состояния без побочных эффектов в виде ImportResult (проверка «как если бы уже отражено»). Используйте её для живых значков ошибок.
  • Публичные Session / Callbacks / Baselines — пользовательские провайдеры источников используют их, чтобы собрать свои цели отражения.

AuthoringDispatchCallbacks — контракт вашего интерфейса

Набор из 13 общих делегатов, которые диспетчер вызывает для каждой заботы, связанной с представлением: ResolveSettings, диалоги подтверждения (ConfirmKeyRenames, ConfirmTabRenames, …), RenderReport (Action<ImportReport> — допускает null, это лишь наблюдение), PushApprover, TriggerReimport, ClearUndo, Rebuild и так далее.

14 диалоговых делегатов, специфичных для встроенных источников Local/Google, вынесены в отдельный опциональный набор BuiltInSourceDialogs — внешней поверхности или провайдеру никогда не нужно их привязывать.

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

Материал для графа

Для проекции вида «узел = запись, ребро = ссылка ∪ объявление»:

  • ReferenceScanner (Core) — единственный источник истины для перечисления вхождений ссылок по всем таблицам: скаляры, элементы списков, явные значения по умолчанию. То же самое перечисление использует валидатор ссылок, поэтому ваш граф и проверка согласованы по построению. Scan(tables) / ScanTable / ScanField / IsReferenceField.
  • IEdgeContributor / EdgeSpec / EdgeContributorRegistry (Core) — доменные плагины объявляют рёбра, которые сканер не видит (внутри значений пользовательских типов, связей через столбец type, рёбер записи с записью полезной нагрузки). Собирайте их через PluginRegistry.BuildEdgeContributors редактора.
  • ReferenceIndex / RecordEdge (Core) — собранный снимок, на котором работает собственный холст Data Studio: Build(...) один раз объединяет отсканированные ссылки с рёбрами от поставщиков, после чего OutEdges / InEdges / InCount отвечают за O(1) на запись. Полный список членов — в Справочнике по API.
  • IRecordCanvasAugmenter / CanvasAugmentBuilder (Core) — контракт переопределения для вкладки, если вы хотите, чтобы доменные пакеты расширяли ваш холст так же, как они расширяют холст Studio (виртуальные узлы, дополнительные рёбра, подсказки по слою и отображению).
  • ProjectionErrorMapper (Editor, чистый) — сопоставляет физическую координату ошибки проекции (вкладка/строка/поле) логическому адресу (вкладка/RecordId/поле), чтобы вы могли привязывать значки ошибок к узлам, а не к номерам строк.

Вспомогательные элементы

ТипДля чего использует ваша поверхность
ImportEventsДве шины, обе — публичные контракты. ImportCompleted (ImportCompletedArgs: Tabs · BakeFolder) срабатывает, когда автоцепочка прошла весь путь до запекания, так что подписчик может читать запечённые ассеты. BaselineUpdated (BaselineUpdatedArgs: Tabs · Quarantined) срабатывает всякий раз, когда снимок таблицы был сохранён — включая прогон, провалившийся проверку, — на это подписывается поверхность, которая хочет показать проваленные таблицы и дать людям их исправить. Подпишитесь на обе, если ваше представление показывает и таблицы, и запечённые значения; отписывайтесь симметрично в OnDisable.
IPipelineObserverЕсли знать об этом должен плагин, а не окно, — это более лёгкий путь: зарегистрируйте наблюдателя и получайте неизменяемый PipelineRunView в конце каждого цикла импорта, вообще без зависимости от редактора — это работает и в браузерном хосте. См. Создание плагинов §4.17.
RecordIdMinterПредлагает id для новых записей — определение префикса + безопасная от коллизий уникализация. API для подсказок, намеренно не автонумерация.
EphemeralSoApplyВременно предпросматривает подготовленные значения поверх запечённых SO (повторный импорт восстанавливает исходное). Применяет вычислимое подмножество; возвращает причины пропуска для столбцов в ожидании и ошибок разбора. Ничто в поставляемом интерфейсе больше не использует его, так что поверхность, которой нужен этот предпросмотр, сама владеет кнопкой для него.
KeyRenamePlannerПланирует переименования ключей (3 этапа: извлечение / распространение / точечное применение) — так же, как это делает поставляемое окно. Подтверждения переименования вкладок проходят через публичный колбэк ConfirmTabRenames.
SourceProviderRegistryРазрешает активный провайдер источника точно так же, как это делает интерфейс настроек.

Базовые правила, которые обеспечивает ядро (и которые вы наследуете)

  • Таблица остаётся канонической — ваша поверхность готовит изменения и отражает их; она никогда не пишет в SO.
  • Сначала проверка, потом отражениеReflect() ничего не записывает, если предварительная проверка не проходит.
  • Никаких молчаливых потерь — неразрешимые правки изолируются с указанием причины; подтверждения проходят через ваши колбэки.
  • Undo интегрируется нативно — храните сессию в сериализуемом поле и регистрируйте снимки undo на своём окне; ClearUndo отмечает границу отражения.
  • Независимость от домена — ядро не содержит ни одного домен-специфичного термина (это проверяется защитными тестами). Ваш домен подключается через контракты плагинов, а не через правки ядра.

Похожие страницы

  • Справочник по API — сигнатуры всего, что упомянуто здесь
  • Создание плагинов — контракты, которые ваш домен использует наряду с ядром
  • Data Studio — поведение, которое ваша поверхность повторяет или заменяет