Saltar al contenido
SheetForge

El núcleo de creación — Cómo construir una segunda superficie de creación

Avanzado. Para autores de assets/herramientas que quieran construir su propia UI de creación (por ejemplo, un lienzo de grafo de nodos) sobre el motor de SheetForge. Los equipos de juego que usan Data Studio no necesitan esta página.

La ventana de creación no es el motor. Data Studio — y la app web junto a ella — son consumidoras de un núcleo de creación agnóstico a la ventana.

Todo lo que hacen se ejecuta a través de tipos públicos que una tercera superficie puede utilizar de la misma manera: preparación, validación, orquestación del reflejo, límites de deshacer, reimportación. Dos superficies ya lo hacen, lo cual es la prueba práctica de que esta costura es real y no aspiracional.

Antes de construir toda una superficie, comprueba si un punto de extensión ya cubre la necesidad. Un plugin puede añadir verbos, paneles, insignias y widgets de celda a la ventana incluida sin poseer una ventana en absoluto, descritos como datos para que se rendericen en el editor y en el navegador — consulta Creación de plugins §4.16. Esta página es para el caso en que quieras tu propio lienzo.

Un ensamblado de prueba de simulación de consumidor (SheetForge.Tests.Consumer, sin acceso InternalsVisibleTo a Core ni a Editor) implementa una superficie de creación virtual de principio a fin, contra la API pública únicamente. Si algún miembro que necesita fuera internal, ese ensamblado no compilaría (CS0122), así que sirve como la especificación ejecutable de la superficie descrita a continuación.

El motor de tres objetos

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

AuthoringSession — el estado de preparación

Una clase simple [Serializable] (deliberadamente no un ScriptableObject): guárdala en un campo [SerializeField] de tu EditorWindow y obtienes instantáneas de Undo nativas de Unity y supervivencia al domain reload de forma gratuita — el mismo mecanismo detrás del Ctrl+Z de las ventanas incluidas.

Posee todo el estado en preparación:

  • ediciones de celda (Edits), filas nuevas (NewRows), operaciones de estructura (StructOps);
  • reordenamientos por pestaña (Reorders), renombrados de pestaña (TabRenames);
  • anclas de baseline, ediciones aisladas.

Además de ese estado, expone la API de mutación/consulta:

  • SetStaged(...) — prepara una edición de celda. Las ediciones llevan una dirección lógica (pestaña · RecordId · campo); el ordinal de fila físico es una caché derivada que se vuelve a resolver justo antes del reflejo.
  • ResolveBaselineEdits(provider) — vuelve a anclar todas las ediciones contra el baseline actual. Las ediciones resolubles continúan; los tres casos irresolubles (renombrado externo / eliminación externa / conflicto de clave) se mueven a IsolatedEdits — excluidas del reflejo, marcadas, nunca descartadas en silencio, nunca bloqueantes de la sesión.
  • Superficie de lectura del baseline: TabNames, TryGetBaselineTable(tab, out SheetTable) — acceso tipado al esquema (TypeToken, @desc, @overlap) sin tocar el analizador tú mismo.
  • EffectiveStructOps() / PendingStructCount() — la vista compuesta y canónica de las operaciones de estructura.
  • Los hooks de remapeo (RemapFieldName / RemapRecordId / RemapTab) mantienen el estado en preparación coherente a través de los renombrados.
  • LastProjectionResult almacena en caché la última proyección.

AuthoringDispatcher — la orquestación del reflejo

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

Reflect() ejecuta el ciclo completo, en orden:

  • validación previa
  • reflejo por origen — escrituras quirúrgicas para lo local, reescritura segura para Google, tu propio destino para proveedores personalizados
  • limpieza de estado retenido
  • el límite de confirmación ClearUndo
  • reimportación automática con informe

Además:

  • BuildProjectionResult() — una proyección sin efectos secundarios del estado en preparación actual, como un ImportResult (validar-como-si-se-hubiera-reflejado). Úsala para insignias de error en vivo.
  • Los Session / Callbacks / Baselines públicos — los proveedores de origen personalizados los usan para ensamblar sus destinos de reflejo.

AuthoringDispatchCallbacks — el contrato de tu UI

Un paquete de 13 delegados generales que el dispatcher invoca para cada aspecto de la vista: ResolveSettings, diálogos de confirmación (ConfirmKeyRenames, ConfirmTabRenames, …), RenderReport (un Action<ImportReport> — tolera null, es meramente observacional), PushApprover, TriggerReimport, ClearUndo, Rebuild, y así sucesivamente.

Los 14 delegados de diálogo específicos de los orígenes integrados Local/Google viven en un paquete BuiltInSourceDialogs separado y opcional, que una superficie externa o un proveedor nunca necesita vincular.

Las ventanas incluidas vinculan valores predeterminados que muestran diálogos; tu lienzo vincula los suyos propios (o no-ops). El motor nunca dibuja la UI por sí mismo.

Material de grafo

Para una proyección de tipo "nodo = registro, arista = referencia ∪ declaración":

  • ReferenceScanner (Core) — la única fuente de verdad para enumerar las ocurrencias de referencias en todas las tablas: escalares, elementos de lista, valores predeterminados explícitos. La misma enumeración que usa el validador de referencias, de modo que tu grafo y la validación concuerdan por construcción. Scan(tables) / ScanTable / ScanField / IsReferenceField.
  • IEdgeContributor / EdgeSpec / EdgeContributorRegistry (Core) — los plugins de dominio declaran aristas que el escáner no puede ver (dentro de valores de tipo personalizado, enlaces de columna type, aristas de registro con un registro de carga útil). Recógelas mediante PluginRegistry.BuildEdgeContributors del Editor.
  • ReferenceIndex / RecordEdge (Core) — la instantánea ensamblada sobre la que corre el propio lienzo de Data Studio: Build(...) fusiona las referencias escaneadas con las aristas de los contribuyentes una sola vez, y luego OutEdges / InEdges / InCount responden en O(1) por registro. Lista completa de miembros en la Referencia de la API.
  • IRecordCanvasAugmenter / CanvasAugmentBuilder (Core) — el contrato de override por pestaña, si quieres que los paquetes de dominio extiendan tu lienzo de la misma manera que extienden el de Studio (nodos virtuales, aristas adicionales, indicios de capa y visualización).
  • ProjectionErrorMapper (Editor, puro) — mapea la coordenada física de un error de proyección (pestaña/fila/campo) a una dirección lógica (pestaña/RecordId/campo), de modo que puedes anclar las insignias de error a los nodos en lugar de a números de fila.

Piezas de apoyo

TipoPara qué lo usa tu superficie
ImportEventsDos buses, ambos contratos públicos. ImportCompleted (ImportCompletedArgs: Tabs · BakeFolder) se dispara cuando la cadena automática ha terminado de pasar por el bake, así que un suscriptor puede leer los assets generados mediante bake. BaselineUpdated (BaselineUpdatedArgs: Tabs · Quarantined) se dispara siempre que se guardó una instantánea de hoja — incluida una ejecución que falló la validación — que es a lo que se suscribe una superficie si quiere mostrar las hojas fallidas y dejar que la gente las corrija. Suscríbete a ambos si tu vista muestra hojas y valores generados mediante bake; cancela la suscripción simétricamente en OnDisable.
IPipelineObserverSi lo que necesita saberlo es un plugin en lugar de una ventana, este es el camino más ligero: registra un observador y recibe un PipelineRunView inmutable al final de cada ciclo de importación, sin ninguna dependencia del editor — también funciona en el host del navegador. Consulta Creación de plugins §4.17.
RecordIdMinterSugiere ids para registros nuevos — detección de prefijo + generación de valores únicos a prueba de colisiones. Una API de sugerencia, deliberadamente no una numeración automática.
EphemeralSoApplyPrevisualiza valores en preparación sobre los SO generados mediante bake, temporalmente (la reimportación restaura). Aplica el subconjunto calculable; devuelve razones de omisión para columnas pendientes y errores de análisis. Nada en la UI incluida lo activa ya, así que una superficie que quiera esta vista previa posee el botón para ella.
KeyRenamePlannerPlanifica renombrados de clave (3 etapas: extracción / propagación / cirugía), igual que las ventanas incluidas. Las confirmaciones de renombrado de pestaña fluyen en cambio a través del callback público ConfirmTabRenames.
SourceProviderRegistryResuelve el proveedor de origen activo de la misma manera que lo hace la UI de ajustes.

Reglas básicas que el núcleo impone (y que tú heredas)

  • La hoja se mantiene canónica — tu superficie prepara y refleja; nunca escribe los SO.
  • Validar y luego reflejarReflect() no escribe nada si la validación previa falla.
  • Sin pérdida silenciosa — las ediciones irresolubles se aíslan con un motivo; las confirmaciones pasan a través de tus callbacks.
  • Undo se integra de forma nativa — mantén la sesión en un campo serializado y registra instantáneas de undo en tu ventana; ClearUndo marca el límite del reflejo.
  • Agnóstico al dominio — el núcleo no contiene ningún vocabulario de dominio (verificado por pruebas de guardia). Tu dominio llega mediante los contratos de plugin, no mediante modificaciones al núcleo.

Páginas relacionadas