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
IEditorView
managed byAssetEditorsManager
is taking up the upper part of the docking grid
- Solution Explorer
- status bar
PluginService
manages plugins registered in AssetsPlugin
. A plugin may register IAssetEditorViewModel
s, and AssetViewModel
s. 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 anEntityHierarchyViewModel
editor.SceneEditorViewModel
- View model of aSceneViewModel
editor. Corresponds toSceneEditorView
, managed bySceneEditorController
.PrefabEditorViewModel
- View model of aPrefabViewModel
editor. Corresponds toPrefabEditorView
, managed byPrefabEditorController
.
UIEditorBaseViewModel
- Base class for the view model of anUIBaseViewModel
editor.UILibraryEditorViewModel
- View model for aUILibraryViewModel
editor. Editor for custom UI controls.UIPageEditorViewModel
- View model for aUIPageViewModel
editor.
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 AssetObjectNode
s. 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 NodeViewModelTemplateProvider
s. 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 NodeViewModel
s 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
WindowManager
with newApp
and run the App - dispatch async
Startup()
- initialize language settings
- create
GameStudioViewModel
for theGameStudioWindow
- add asset plugins
StrideEditorPlugin
creates 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
AssetEditorsManager
usesIAssetsPluginService
implementationStride.Core.Assets.Editor.Services.PluginService
to 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