Overview
Frosty Toolsuite provides two main approaches to customizing the editing experience:
Asset Editors : Full custom editors for specific asset types (inherit from FrostyAssetEditor)
Type Editors : Custom property grid editors for specific field types (inherit from FrostyTypeEditor<T>)
FrostyAssetEditor
Create custom editors for entire asset types by inheriting from FrostyAssetEditor.
Base Class
public class FrostyAssetEditor : Control
Constructor
public FrostyAssetEditor ( ILogger inLogger )
The logger instance for the editor to use
Properties
RootObject
public object RootObject { get ; }
Gets the root object of the currently loaded asset.
RootObjects
public IEnumerable < object > RootObjects { get ; }
Gets all root objects in the asset (some assets may have multiple roots).
Objects
public IEnumerable < object > Objects { get ; }
Gets all objects in the asset, including nested objects.
AssetEntry
public AssetEntry AssetEntry { get ; }
Gets the asset entry for the currently loaded asset.
Asset
public EbxAsset Asset { get ; }
Gets the loaded EBX asset.
AssetModified
public bool AssetModified { get ; set ; }
Set to true to mark the asset as modified and save changes to the AssetManager.
Methods
SetAsset
public int SetAsset ( AssetEntry entry )
Loads the specified asset into the editor. Called automatically by the editor framework.
LoadAsset
protected virtual EbxAsset LoadAsset ( EbxAssetEntry entry )
Override to customize how the asset is loaded.
AddDependentObject
public void AddDependentObject ( Guid guid )
Adds a dependent object to the asset and loads it.
The GUID of the asset to add as a dependency
GetDependentObject
public virtual EbxAsset GetDependentObject ( Guid guid )
Retrieves a dependent object by GUID.
The GUID of the dependent asset
The dependent asset, or null if not found
RefreshDependentObject
public virtual EbxAsset RefreshDependentObject ( Guid guid )
Reloads a dependent object from the AssetManager.
The GUID of the dependent asset to refresh
AddObject
public virtual void AddObject ( object obj )
Adds a new object to the asset.
RemoveObject
public virtual void RemoveObject ( object obj )
Removes an object from the asset.
public virtual List < ToolbarItem > RegisterToolbarItems ()
Override to add custom toolbar buttons to the editor.
List of toolbar items to add to the editor
Closed
public virtual void Closed ()
Override to perform cleanup when the editor is closed.
Events
OnAssetModified
public event RoutedEventHandler OnAssetModified
Fired when the asset is modified.
Complete Example
MeshAssetEditor.cs
Themes/MeshAssetEditor.xaml
using Frosty . Core . Controls ;
using FrostySdk . Interfaces ;
using System . Collections . Generic ;
using System . Windows ;
using System . Windows . Controls ;
public class MeshAssetEditor : FrostyAssetEditor
{
private MeshViewport viewport ;
private FrostyPropertyGrid propertyGrid ;
static MeshAssetEditor ()
{
DefaultStyleKeyProperty . OverrideMetadata (
typeof ( MeshAssetEditor ),
new FrameworkPropertyMetadata ( typeof ( MeshAssetEditor )));
}
public MeshAssetEditor ( ILogger inLogger ) : base ( inLogger )
{
}
public override void OnApplyTemplate ()
{
base . OnApplyTemplate ();
viewport = GetTemplateChild ( "PART_Viewport" ) as MeshViewport ;
propertyGrid = GetTemplateChild ( "PART_PropertyGrid" ) as FrostyPropertyGrid ;
if ( viewport != null && Asset != null )
{
viewport . LoadMesh ( Asset . RootObject );
}
if ( propertyGrid != null && Asset != null )
{
propertyGrid . Object = Asset . RootObject ;
}
}
protected override void InvokeOnAssetModified ()
{
base . InvokeOnAssetModified ();
// Refresh viewport when asset changes
if ( viewport != null )
{
viewport . LoadMesh ( Asset . RootObject );
}
}
public override List < ToolbarItem > RegisterToolbarItems ()
{
var items = base . RegisterToolbarItems ();
items . Add ( new ToolbarItem (
"Export Mesh" ,
"Export mesh to FBX" ,
"Images/ExportIcon.png" ,
new RelayCommand ( ExportMesh_Click , ExportMesh_CanClick )));
items . Add ( new ToolbarItem (
"Import Mesh" ,
"Import mesh from FBX" ,
"Images/ImportIcon.png" ,
new RelayCommand ( ImportMesh_Click , ImportMesh_CanClick )));
return items ;
}
private void ExportMesh_Click ( object state )
{
// Export logic
Microsoft . Win32 . SaveFileDialog dialog = new Microsoft . Win32 . SaveFileDialog ()
{
Filter = "FBX Files (*.fbx)|*.fbx" ,
DefaultExt = "fbx"
};
if ( dialog . ShowDialog () == true )
{
// Export mesh
ExportToFbx ( Asset . RootObject , dialog . FileName );
}
}
private bool ExportMesh_CanClick ( object state ) => true ;
private void ImportMesh_Click ( object state )
{
// Import logic
Microsoft . Win32 . OpenFileDialog dialog = new Microsoft . Win32 . OpenFileDialog ()
{
Filter = "FBX Files (*.fbx)|*.fbx" ,
DefaultExt = "fbx"
};
if ( dialog . ShowDialog () == true )
{
// Import mesh
ImportFromFbx ( Asset . RootObject , dialog . FileName );
AssetModified = true ;
}
}
private bool ImportMesh_CanClick ( object state ) => true ;
public override void Closed ()
{
base . Closed ();
// Clean up viewport resources
viewport ? . Dispose ();
}
}
FrostyTypeEditor
Create custom property grid editors for specific field types.
Base Class
public abstract class FrostyTypeEditor < T > : FrostyBaseTypeEditor where T : FrameworkElement , new ()
Properties
The property path to bind to on the item data
BindingMode
BindingMode
default: "TwoWay"
The binding mode for the value property
The dependency property to bind to on the editor control
Optional value converter for the binding
Parameter to pass to the value converter
Optional validation rule for the binding
Whether to notify when the binding target is updated
Methods
CreateEditor
public T CreateEditor ( FrostyPropertyGridItemData item )
Creates the editor control. Called automatically by the property grid.
CustomizeEditor
protected virtual void CustomizeEditor ( T editor , FrostyPropertyGridItemData item )
Override to customize the editor after creation.
RefreshEditor
protected virtual void RefreshEditor ( T editor )
Override to refresh the editor when the value changes.
Registration
Register type editors globally using the RegisterGlobalTypeEditorAttribute:
using Frosty . Core . Attributes ;
[ assembly : RegisterGlobalTypeEditor ( "Vec3" , typeof ( Vec3Editor ))]
[ assembly : RegisterGlobalTypeEditor ( "LinearTransform" , typeof ( LinearTransformEditor ))]
Example: Vec3 Editor
Vec3Editor.cs
Themes/Vec3EditorControl.xaml
using Frosty . Core . Controls . Editors ;
using System . Windows ;
using System . Windows . Controls ;
using System . Windows . Data ;
public class Vec3Editor : FrostyTypeEditor < Vec3EditorControl >
{
public Vec3Editor ()
{
ValueProperty = Vec3EditorControl . ValueProperty ;
}
protected override void CustomizeEditor ( Vec3EditorControl editor , FrostyPropertyGridItemData item )
{
base . CustomizeEditor ( editor , item );
// Additional customization
editor . ShowLabels = true ;
}
}
public class Vec3EditorControl : Control
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty . Register (
"Value" ,
typeof ( object ),
typeof ( Vec3EditorControl ),
new PropertyMetadata ( null , OnValueChanged ));
public object Value
{
get => GetValue ( ValueProperty );
set => SetValue ( ValueProperty , value );
}
public bool ShowLabels { get ; set ; } = true ;
private TextBox xBox , yBox , zBox ;
static Vec3EditorControl ()
{
DefaultStyleKeyProperty . OverrideMetadata (
typeof ( Vec3EditorControl ),
new FrameworkPropertyMetadata ( typeof ( Vec3EditorControl )));
}
public override void OnApplyTemplate ()
{
base . OnApplyTemplate ();
xBox = GetTemplateChild ( "PART_X" ) as TextBox ;
yBox = GetTemplateChild ( "PART_Y" ) as TextBox ;
zBox = GetTemplateChild ( "PART_Z" ) as TextBox ;
if ( xBox != null )
{
xBox . TextChanged += ( s , e ) => UpdateValue ();
}
if ( yBox != null )
{
yBox . TextChanged += ( s , e ) => UpdateValue ();
}
if ( zBox != null )
{
zBox . TextChanged += ( s , e ) => UpdateValue ();
}
UpdateDisplay ();
}
private static void OnValueChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
{
( d as Vec3EditorControl ) ? . UpdateDisplay ();
}
private void UpdateDisplay ()
{
if ( Value == null || xBox == null )
return ;
dynamic vec = Value ;
xBox . Text = vec . x . ToString ();
yBox . Text = vec . y . ToString ();
zBox . Text = vec . z . ToString ();
}
private void UpdateValue ()
{
if ( Value == null || xBox == null )
return ;
dynamic vec = Value ;
if ( float . TryParse ( xBox . Text , out float x ))
vec . x = x ;
if ( float . TryParse ( yBox . Text , out float y ))
vec . y = y ;
if ( float . TryParse ( zBox . Text , out float z ))
vec . z = z ;
}
}
Define custom toolbar buttons for asset editors.
public class ToolbarItem
{
public string Text { get ; }
public string ToolTip { get ; }
public ImageSource Icon { get ; }
public RelayCommand Command { get ; }
public ToolbarItem ( string text , string tooltip , string icon , RelayCommand inCommand )
}
Path to icon image relative to plugin assembly
Command to execute when clicked
See Also