Le noyau de création — construire une seconde surface de création
Avancé. Pour les auteurs d'assets/d'outils qui veulent construire leur propre interface de création (par exemple, un canevas de graphe à nœuds) par-dessus le moteur de SheetForge. Les équipes de jeu qui utilisent le Data Studio n'ont pas besoin de cette page.
La fenêtre de création n'est pas le moteur. Le Data Studio — et l'application web à ses côtés — sont des consommateurs d'un noyau de création indépendant de toute fenêtre.
Tout ce qu'ils font passe par des types publics qu'une troisième surface peut piloter de la même façon : préparation, validation, orchestration de la répercussion, limites d'annulation, réimport. Deux surfaces le font déjà, ce qui est la preuve pratique que la couture est réelle plutôt qu'aspirationnelle.
Avant de construire toute une surface, vérifiez si un point d'extension couvre déjà le besoin. Un plugin peut ajouter des verbes, des panneaux, des badges et des widgets de cellule à la fenêtre livrée sans posséder de fenêtre du tout, décrits comme des données afin qu'ils se dessinent dans l'éditeur et dans le navigateur — voir Création de plugins §4.16. Cette page est pour le cas où vous voulez votre propre canevas.
Un assembly de test de simulation de consommateur (SheetForge.Tests.Consumer, sans accès InternalsVisibleTo au Core ou à l'Editor) implémente une surface de création virtuelle de bout en bout contre la seule API publique. Si un membre dont il a besoin était internal, cet assembly ne compilerait pas (CS0122). Il sert donc de spécification exécutable pour la surface décrite ci-dessous.
Le moteur à trois objets
┌─────────────────────┐ ┌──────────────────────────┐ ┌───────────────┐
│ AuthoringSession │────▶│ AuthoringDispatcher │────▶│ BaselineStore │
│ (staging state) │ │ .Reflect() │ │ (round-trip │
│ │ │ (the full cycle) │ │ snapshots) │
└─────────────────────┘ └────────────┬─────────────┘ └───────────────┘
│ binds
┌────────────▼─────────────┐
│ AuthoringDispatchCallbacks│
│ (view concerns — YOUR UI) │
└──────────────────────────┘AuthoringSession — l'état de préparation
Une simple classe [Serializable], volontairement pas un ScriptableObject. Conservez-la dans un champ [SerializeField] de votre EditorWindow, et vous obtenez gratuitement des instantanés Undo natifs d'Unity et une survie au rechargement de domaine — le même mécanisme qui se cache derrière le Ctrl+Z des fenêtres livrées.
Elle possède tout l'état préparé :
- modifications de cellule (
Edits), nouvelles lignes (NewRows), opérations de structure (StructOps) ; - réorganisations par onglet (
Reorders), renommages d'onglet (TabRenames) ; - ancres de baseline, modifications isolées.
Au-dessus de cet état, elle expose l'API de mutation/interrogation :
SetStaged(...)— prépare une modification de cellule. Les modifications portent une adresse logique (onglet · RecordId · champ) ; l'ordinal de ligne physique est un cache dérivé, résolu à nouveau juste avant la répercussion.ResolveBaselineEdits(provider)— réancre toutes les modifications par rapport à la baseline actuelle. Les modifications résolubles avancent. Les trois cas irrésolubles (renommage externe / suppression externe / conflit de clé) sont déplacés versIsolatedEdits: exclus de la répercussion, signalés par un badge, jamais abandonnés silencieusement, jamais bloquants pour la session.- Surface de lecture de la baseline :
TabNames,TryGetBaselineTable(tab, out SheetTable)— accès au schéma typé (TypeToken,@desc,@overlap) sans avoir à toucher vous-même au parseur. EffectiveStructOps()/PendingStructCount()— la vue composée et canonique des opérations de structure.- Les hooks de remappage (
RemapFieldName/RemapRecordId/RemapTab) gardent l'état préparé cohérent à travers les renommages. LastProjectionResultmet en cache la dernière projection.
AuthoringDispatcher — l'orchestration de la répercussion
var dispatcher = new AuthoringDispatcher(session, callbacks, baselineStore);
dispatcher.Reflect(); // the entire cycle, one callReflect() exécute le cycle complet, dans l'ordre :
- validation préalable
- répercussion par source — écritures chirurgicales pour le local, réécriture sûre pour Google, votre propre cible pour les fournisseurs personnalisés
- nettoyage de l'état conservé
- la limite de confirmation
ClearUndo - réimport automatique avec rapport
De plus :
BuildProjectionResult()— une projection sans effet de bord de l'état préparé actuel sous forme d'ImportResult(valider comme si la répercussion avait eu lieu). Utilisez-la pour des badges d'erreur en direct.Session/Callbacks/Baselinespublics — les fournisseurs de source personnalisés les utilisent pour assembler leurs cibles de répercussion.
AuthoringDispatchCallbacks — le contrat de votre interface
Un ensemble de 13 délégués généraux que le dispatcher appelle pour chaque préoccupation de vue : ResolveSettings, des boîtes de dialogue de confirmation (ConfirmKeyRenames, ConfirmTabRenames, …), RenderReport (un Action<ImportReport> — tolérant au null, purement observationnel), PushApprover, TriggerReimport, ClearUndo, Rebuild, et ainsi de suite.
Les 14 délégués de boîte de dialogue propres aux sources intégrées Local/Google vivent dans un ensemble séparé et optionnel, BuiltInSourceDialogs, qu'une surface externe ou un fournisseur n'a jamais besoin de lier.
Les fenêtres livrées lient des valeurs par défaut qui affichent des boîtes de dialogue ; votre canevas lie les siennes (ou des no-op). Le moteur ne dessine jamais lui-même d'interface.
Matière de graphe
Pour une projection « nœud = enregistrement, arête = référence ∪ déclaration » :
ReferenceScanner(Core) — la source unique de vérité pour énumérer les occurrences de référence à travers toutes les tables : scalaires, éléments de liste, valeurs par défaut explicites. C'est la même énumération que celle utilisée par le validateur de référence, si bien que votre graphe et la validation concordent par construction.Scan(tables)/ScanTable/ScanField/IsReferenceField.IEdgeContributor/EdgeSpec/EdgeContributorRegistry(Core) — les plugins de domaine déclarent les arêtes que le scanner ne peut pas voir (à l'intérieur de valeurs de type personnalisé, liens de colonnetype, arêtes d'enregistrement avec un enregistrement de charge utile). Collectez-les viaPluginRegistry.BuildEdgeContributorsde l'Editor.ReferenceIndex/RecordEdge(Core) — l'instantané assemblé sur lequel tourne le propre canevas du Data Studio :Build(...)fusionne une fois les références scannées avec les arêtes des contributeurs, puisOutEdges/InEdges/InCountrépondent en O(1) par enregistrement. Liste complète des membres dans la Référence de l'API.IRecordCanvasAugmenter/CanvasAugmentBuilder(Core) — le contrat de surcharge par onglet, si vous voulez que des packs de domaine étendent votre canevas de la même façon qu'ils étendent celui du Studio (nœuds virtuels, arêtes supplémentaires, indices de calque et d'affichage).ProjectionErrorMapper(Editor, pur) — fait correspondre la coordonnée physique d'une erreur de projection (onglet/ligne/champ) à une adresse logique (onglet/RecordId/champ), afin que vous puissiez épingler les badges d'erreur aux nœuds plutôt qu'à des numéros de ligne.
Éléments de support
| Type | À quoi votre surface s'en sert |
|---|---|
ImportEvents | Deux bus, tous deux des contrats publics. ImportCompleted (ImportCompletedArgs : Tabs · BakeFolder) se déclenche quand la chaîne automatique est allée jusqu'au bout du bake, si bien qu'un abonné peut lire les assets bakés. BaselineUpdated (BaselineUpdatedArgs : Tabs · Quarantined) se déclenche chaque fois qu'un instantané de feuille a été enregistré — y compris une exécution qui a échoué à la validation — ce à quoi une surface s'abonne si elle veut montrer les feuilles en échec et laisser les gens les corriger. Abonnez-vous aux deux si votre vue montre à la fois des feuilles et des valeurs bakées ; désabonnez-vous symétriquement dans OnDisable. |
IPipelineObserver | Si ce qui a besoin de savoir est un plugin plutôt qu'une fenêtre, c'est le chemin le plus léger : enregistrez un observateur et recevez un PipelineRunView immuable à la fin de chaque cycle d'import, sans aucune dépendance à l'éditeur — cela fonctionne aussi dans l'hôte navigateur. Voir Création de plugins §4.17. |
RecordIdMinter | Suggère des id pour les nouveaux enregistrements — détection de préfixe + unification sans collision. Une API de suggestion, volontairement pas une numérotation automatique. |
EphemeralSoApply | Prévisualise temporairement les valeurs préparées sur les SO bake (le réimport les restaure). Applique le sous-ensemble calculable ; renvoie des raisons de saut pour les colonnes en attente et les échecs de parsing. Plus rien dans l'UI livrée ne le pilote, si bien qu'une surface qui veut cette prévisualisation possède elle-même le bouton pour cela. |
KeyRenamePlanner | Planifie les renommages de clé (3 étapes : extraction / propagation / chirurgie), de la même façon que les fenêtres livrées. Les confirmations de renommage d'onglet passent plutôt par le callback public ConfirmTabRenames. |
SourceProviderRegistry | Résout le fournisseur de source actif de la même façon que l'interface des paramètres. |
Règles de base imposées par le noyau (et que vous héritez)
- La feuille reste canonique — votre surface prépare et répercute ; elle n'écrit jamais dans les SO.
- Valider puis répercuter —
Reflect()n'écrit rien si la validation préalable échoue. - Aucune perte silencieuse — les modifications irrésolubles s'isolent avec une raison ; les confirmations passent par vos callbacks.
- L'annulation s'intègre nativement — conservez la session dans un champ sérialisé et enregistrez des instantanés d'annulation sur votre fenêtre ;
ClearUndomarque la limite de répercussion. - Indépendant du domaine — le noyau ne contient aucun vocabulaire de domaine (testé par un garde-fou). Votre domaine arrive via les contrats de plugin, pas via des modifications du noyau.
Pages associées
- Référence de l'API — les signatures de tout ce qui est nommé ici
- Création de plugins — les contrats que votre domaine utilise aux côtés du noyau
- Data Studio — le comportement que votre surface reproduit ou remplace