This document is meant to describe the architecture of the current Stride Editor, so that it's easier to read its source code and rewrite it in a better way. This is in now way a complete document as the Editor is about 35k LOC. I'm slowly going to read through the more interesting parts and note down my thoughts.
GameStudioWindow- static top bar menu
- static asset context menu
- static key bindings (start/cancel build, run game, open debug window, copy asset, show add asset dialog)
- static toolbar tray (icon buttons)
- routed commands (new, open, save, undo, redo)
- docking manager (AvalonDock)
- Solution Explorer
- buttons
- tree view (with context menu)
- Asset View via
AssetViewUserControl - References via
AssetViewUserControl - Asset log via
GridLogViewer - Build log via
TextLogViewer- tabbed (BuildLog, LiveScriptingLog)
- Property Grid
- the whole grid is implemented here
- Asset Preview via
ContentPresenter - Action History
- listbox with a data template
- any instance of
IEditorViewmanaged byAssetEditorsManageris taking up the upper part of the docking grid
- Solution Explorer
- status bar
PluginService manages plugins registered in AssetsPlugin. A plugin may register IAssetEditorViewModels, and AssetViewModels. The registering methods look within the plugins assembly for types to register.
There's 3 plugins implemented that extend on AssetsPlugin functionality:
Stride.Core.Assets.Editor.AssetsEditorPlugin- registers some resources and paste processorsStride.Editor.StrideEditorPlugin- creates services (GameSettings, GameStudioBuilder, GameStudioPreview, GameStudioThumbnail, StrideDebug)
Stride.Assets.Presentation.StrideDefaultAssetsPlugin- (among others) registers node presenters/updaters for some more advanced assets, suggested packages (that your project can reference), copy/paste processors, effect settings
Stride.Assets.Presentation.GameEditorViewModel- Base class for the view model of asset editors that runs an instance of a Game.AssetCompositeEditorViewModel- Base class for the view model of anAssetCompositeViewModel<TAsset>editor.AssetCompositeHierarchyEditorViewModel<TAssetPartDesign, TAssetPart, TItemViewModel>- Base class for the view model of anAssetCompositeHierarchyViewModel<TAssetPartDesign,TAssetPart>editor.EntityHierarchyEditorViewModel- Base class for the view model of anEntityHierarchyViewModeleditor.SceneEditorViewModel- View model of aSceneViewModeleditor. Corresponds toSceneEditorView, managed bySceneEditorController.PrefabEditorViewModel- View model of aPrefabViewModeleditor. Corresponds toPrefabEditorView, managed byPrefabEditorController.
UIEditorBaseViewModel- Base class for the view model of anUIBaseViewModeleditor.UILibraryEditorViewModel- View model for aUILibraryViewModeleditor. Editor for custom UI controls.UIPageEditorViewModel- View model for aUIPageViewModeleditor.
Stride.Assets.Presentation.GraphicsCompositorEditorViewModel- graph editor for theGraphicsCompositorAsset.Stride.Assets.Presentation.ScriptEditorViewModel- View model for the script editor (using Roslyn & AvalonEdit and RoslynPad).Stride.Assets.Presentation.SpriteSheetEditorViewModel- sprite region selection tool for images that creates a sprite sheet.Stride.Assets.Presentation.VisualScriptEditorViewModel- editor ofVisualScriptAsset
First we need to understand how the graph works. A graph is a set of nodes connected by edges (here by members). Here our nodes implement Stride.Core.Quantum.IGraphNode. An object can be represented by a IObjectNode with its properties as IMemberNode. The nodes are stored in a INodeContainer with a INodeBuilder that creates IObjectNode from an object. If the object node is a collection or a dictionary it will have ItemReferences populated with references to those child items.
So basically, we are recreating the object graph in a metadata graph with references to the proper objects.
Then there's the Stride.Core.Assets.Quantum.AssetPropertyGraph composed of AssetObjectNodes. The object nodes are enriched with additional context of AssetObjectNodeExtended which deals with data inheritance/overrides for archetypes/prefabs. The AssetNodeContainer introduces more primitive types (vectors, colors, etc.) - a graph node of a primitive type is always a leaf.
There's a little more about how AssetPropertyGraph should behave described in a AssetPropertyGraphDefinition. In particular Stride.Assets.Presentation.Quantum.GraphicsCompositorAssetPropertyGraphDefinition describes which types of its members need to be references.
The Stride.Core.Assets.Quantum.AssetQuantumRegistry is responsible for constructing the AssetPropertyGraph into a AssetPropertyGraphContainer. Depending on the asset type (there's an attribute mapping asset property graph to type of asset) we can get an extension on the graph - currently there's EntityHierarchyPropertyGraph and UIAssetPropertyGraph (in Stride.Assets.Presentation.Quantum) for their hierarchical assets.
Note: here we can see my issue with domain boundries - EntityHierarchyPropertyGraph is coupled with presentation logic. For the rewrite it will have to be decoupled and logical parts moved into Stride.Core.Assets.Quantum.
Now that we have a graph, we want to show it. This will be WPF dependent code. The projects involved are: Stride.Core.Presentation, Stride.Core.Presentation.Quantum, Stride.Core.Assets.Editor (Quantum) and Stride.Assets.Presentation. Presentation.Quantum is mostly abstract and doesn't directly depend on WPF.
Nodes of a graph are included in the Stride.Core.Presentation.Quantum.ViewModels.NodeViewModel and presented with a Stride.Core.Presentation.Quantum.Presenters.INodePresenter (e.g. ItemNodePresenter for collecions and MemberNodePresenter for members). How it is displayed will depend on the Stride.Core.Presentation.Quantum.View.NodeViewModelTemplateProvider.
For the purpose of enriching the NodeViewModel there are Updaters that allow populating its AssociatedData (by INodePresenter.AttachedProperties.Add()) in Stride.Core.Assets.Editor.Quantum.NodePresenters.Updaters. Any behavior is defined with a command in Stride.Core.Assets.Editor.Quantum.NodePresenters.Commands. Commands and Updaters are registered in Stride.Core.Assets.Editor.Components.Properties.PropertiesViewModel (and its subclasses).
The actual XAML views are stored in a template definition which references the NodeViewModelTemplateProviders. The template providers code is defined in Stride.Core.Assets.Editor.View.TemplateProviders and the views are all in one file (14k LOC) Stride.Core.Assets.Editor.View.DefaultPropertyTemplateProviders.xaml. This needs to be divided up. It's included as a ResourceDictionary in Stride.Core.Assets.Editor.View.EditorDialogService.RegisterDefaultTemplateProviders() and called in the constructor of Stride.Core.Assets.Editor.ViewModel.EditorViewModel.
So in the EditorViewModel we access SessionViewModel (Session) on which we can set SessionObjectPropertiesViewModel (AssetViewProperties) which is the data context for the Property Grid (defined in GameStudioWindow's view). It's a subclass of ProperiesViewModel which has a GraphViewModel member, which is constructed with a list of INodePresenter and that is how NodeViewModels are created and displayed.
- check if x64, else exit
- check registry for Privacy Policy acceptance (via
PrivacyPolicyHelper) - initialize
EditorSettings - install Metrics
- load previous settings if project was reloaded
- process arguments
- connection with launcher process
- create new project vs load existing
- debug editor graphics (embedded game window)
- reattach to VS debbuger
- log effects to file
- run module init for
Stride.Core.Assets - create
WindowManagerwith newAppand run the App - dispatch async
Startup()- initialize language settings
- create
GameStudioViewModelfor theGameStudioWindow - add asset plugins
StrideEditorPlugincreates services (GameSettings, GameStudioBuilder, GameStudioPreview, GameStudioThumbnail, StrideDebug)StrideDefaultAssetsPlugin(among others) registers node presenters/updaters for some more advanced assets, suggested packages (that your project can reference), copy/paste processors, effect settings
- show main window or project selection/creation dialog
- create
DockingLayoutManager, which manages where is what window docked- There's an "editor" layout and a "normal" layout.
- create
AssetEditorsManager- This manager is responsible for filling the editor window.
- Each opened assset (identified by
AssetViewModel) has a correspondingIAssetEditorViewModel, and each of those should have a layout pane in the editor. - The
AssetEditorsManagerusesIAssetsPluginServiceimplementationStride.Core.Assets.Editor.Services.PluginServiceto construct a newIEditorView, which during initialization constructs aIAssetEditorViewModel. - It's also possible to have one editor working over multiple assets with
IMultipleAssetEditorViewModel.
- registers asset editors manager in
IEditorDialogService- This service handles tihngs like notifications, settings window, progress window, all dialog windows.
- sets up commands used in XAML
- opens a Metrics session
- handles closing the window, registering assets previews, saving the whole session
- upon being loaded for the first time;
- resizes the window to fit the screen
- loads docking layout
- loads settings (incl. status of opened panes)
- initializes plugins of
IAssetsPluginService - opens previously opened editors
- registers a clipboard monitor listener
- notifies launcher GameStudio has started (launcher may close itself now)
- links other opened windows as children using windows native API