// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Xml; using FlaxEditor.Content; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; using FlaxEditor.GUI; using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Tabs; using FlaxEditor.History; using FlaxEditor.Scripting; using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; namespace FlaxEditor.Surface { /// /// The base interface for editor windows that use for content editing. /// /// public interface IVisjectSurfaceWindow : IVisjectSurfaceOwner { /// /// Gets the asset edited by the window. /// Asset VisjectAsset { get; } /// /// Gets the Visject surface editor. /// VisjectSurface VisjectSurface { get; } /// /// The new parameter types enum type to use. Null to disable adding new parameters. /// IEnumerable NewParameterTypes { get; } /// /// Event called when surface gets loaded (eg. after opening the window). /// event Action SurfaceLoaded; /// /// Called when parameter rename undo action is performed. /// void OnParamRenameUndo(); /// /// Called when parameter edit attributes undo action is performed. /// void OnParamEditAttributesUndo(); /// /// Called when parameter add undo action is performed. /// void OnParamAddUndo(); /// /// Called when parameter remove undo action is performed. /// void OnParamRemoveUndo(); /// /// Gets the asset parameter. /// /// The zero-based parameter index. /// The value. object GetParameter(int index); /// /// Sets the asset parameter. /// /// The zero-based parameter index. /// The value to set. void SetParameter(int index, object value); } /// /// The surface parameter rename action for undo. /// /// sealed class RenameParamAction : IUndoAction { /// /// The window reference. /// public IVisjectSurfaceWindow Window; /// /// The index of the parameter. /// public int Index; /// /// The name before. /// public string Before; /// /// The name after. /// public string After; /// public string ActionString => "Rename parameter"; /// public void Do() { Set(After); } /// public void Undo() { Set(Before); } private void Set(string value) { var param = Window.VisjectSurface.Parameters[Index]; param.Name = value; Window.VisjectSurface.OnParamRenamed(param); Window.OnParamRenameUndo(); } /// public void Dispose() { Window = null; Before = null; After = null; } } /// /// The attributes edit action for undo. /// /// abstract class EditAttributesAction : IUndoAction { /// /// The window reference. /// public IVisjectSurfaceWindow Window; /// /// The attributes before. /// public Attribute[] Before; /// /// The attributes after. /// public Attribute[] After; /// public string ActionString => "Edit attributes"; /// public void Do() { Set(After); } /// public void Undo() { Set(Before); } /// /// Sets the specified attributes. /// /// The value. protected abstract void Set(Attribute[] value); /// public void Dispose() { Window = null; Before = null; After = null; } } /// /// The surface attributes edit action for undo. /// /// sealed class EditSurfaceAttributesAction : EditAttributesAction { /// protected override void Set(Attribute[] value) { Window.VisjectSurface.Context.Meta.SetAttributes(value); Window.VisjectSurface.MarkAsEdited(); } } /// /// The surface node attributes edit action for undo. /// /// sealed class EditNodeAttributesAction : EditAttributesAction { /// /// The id of the node. /// public uint ID; /// protected override void Set(Attribute[] value) { var node = Window.VisjectSurface.FindNode(ID); node.Meta.SetAttributes(value); Window.VisjectSurface.MarkAsEdited(); } } /// /// The surface parameter attributes edit action for undo. /// /// sealed class EditParamAttributesAction : EditAttributesAction { /// /// The index of the parameter. /// public int Index; /// protected override void Set(Attribute[] value) { var param = Window.VisjectSurface.Parameters[Index]; param.Meta.SetAttributes(value); Window.VisjectSurface.OnParamEdited(param); Window.VisjectSurface.MarkAsEdited(); Window.OnParamEditAttributesUndo(); } } /// /// The undo action for adding or removing surface parameter. /// /// /// sealed class AddRemoveParamAction : IUndoAction { /// /// The window reference. /// public IVisjectSurfaceWindow Window; /// /// The identifier of the parameter. Empty to auto generate it. /// public Guid Id = Guid.NewGuid(); /// /// True if adding, false if removing parameter. /// public bool IsAdd; /// /// The index of the parameter. /// public int Index; /// /// The name of the parameter. /// public string Name; /// /// The type of the parameter. /// public ScriptType Type; /// /// The value to initialize the parameter with. Can be null to use default one for the parameter type. /// public object InitValue; /// public string ActionString => IsAdd ? "Add parameter" : "Remove parameter"; /// public void Do() { if (IsAdd) Add(); else Remove(); } /// public void Undo() { if (IsAdd) Remove(); else Add(); } private void Add() { var type = Type; if (IsAdd && type.Type == typeof(NormalMap)) type = new ScriptType(typeof(Texture)); var param = new SurfaceParameter { ID = Id, IsPublic = true, Name = Name, Type = type, Value = InitValue ?? TypeUtils.GetDefaultValue(type), }; if (IsAdd && Type.Type == typeof(NormalMap)) param.Value = FlaxEngine.Content.LoadAsyncInternal("Engine/Textures/NormalTexture"); Window.VisjectSurface.Parameters.Insert(Index, param); Window.VisjectSurface.OnParamCreated(param); Window.OnParamAddUndo(); } private void Remove() { var param = Window.VisjectSurface.Parameters[Index]; if (!IsAdd) { Name = param.Name; Type = param.Type; } Window.VisjectSurface.Parameters.RemoveAt(Index); Window.VisjectSurface.OnParamDeleted(param); Window.OnParamRemoveUndo(); } /// public void Dispose() { Window = null; } } sealed class ReorderParamAction : IUndoAction { /// /// The window reference. /// public IVisjectSurfaceWindow Window; /// /// The parameters editor for this action. /// public ParametersEditor Editor; /// /// The old index the parameter was at. /// public int OldIndex; /// /// The new index the parameter will be at. /// public int NewIndex; /// public string ActionString => "Reorder Parameter"; /// public void Dispose() { Window = null; Editor = null; } public void Swap(int oldIdx, int newIdx) { if (oldIdx == newIdx) return; // ? var parameters = Window.VisjectSurface.Parameters; if (newIdx > oldIdx) { for (int i = oldIdx; i < newIdx; i++) { SurfaceParameter old = parameters[i + 1]; parameters[i + 1] = parameters[i]; parameters[i] = old; } } else { for (int i = oldIdx; i > newIdx; i--) { SurfaceParameter old = parameters[i - 1]; parameters[i - 1] = parameters[i]; parameters[i] = old; } } } /// public void Do() { Swap(OldIndex, NewIndex); Window.VisjectSurface.OnParamReordered(); } /// public void Undo() { Swap(NewIndex, OldIndex); Window.VisjectSurface.OnParamReordered(); } } /// /// Custom draggable property name label that handles reordering visject parameters. /// /// [HideInEditor] public class ParameterPropertyNameLabel : DraggablePropertyNameLabel { private ParametersEditor _editor; private IVisjectSurfaceWindow _window; private Rectangle _arrangeButtonRect; private bool _arrangeButtonInUse; /// public ParameterPropertyNameLabel(string name, ParametersEditor editor) : base(name) { _editor = editor; _window = _editor.Values[0] as IVisjectSurfaceWindow; _arrangeButtonRect = new Rectangle(2, 3, 12, 12); // Extend margin of the label to support a dragging handle Margin m = Margin; m.Left += 16; Margin = m; } private bool ArrangeAreaCheck(out int index, out Rectangle rect) { var child = _editor.ChildrenEditors[0]; var container = child.Layout.ContainerControl; var mousePosition = container.PointFromScreen(Input.MouseScreenPosition); var barSidesExtend = 20.0f; var barHeight = 5.0f; var barCheckAreaHeight = 40.0f; var pos = mousePosition.Y + barCheckAreaHeight * 0.5f; for (int i = 0; i < container.Children.Count / 2; i++) { var containerChild = container.Children[i * 2]; // times 2 to skip the value editor if (Mathf.IsInRange(pos, containerChild.Top, containerChild.Top + barCheckAreaHeight) || (i == 0 && pos < containerChild.Top)) { index = i; var p1 = containerChild.UpperLeft; rect = new Rectangle(PointFromParent(p1) - new Float2(barSidesExtend * 0.5f, barHeight * 0.5f), Width + barSidesExtend, barHeight); return true; } } var p2 = container.Children[((container.Children.Count / 2) - 1) * 2].BottomLeft; if (pos > p2.Y) { index = (container.Children.Count / 2) - 1; rect = new Rectangle(PointFromParent(p2) - new Float2(barSidesExtend * 0.5f, barHeight * 0.5f), Width + barSidesExtend, barHeight); return true; } index = -1; rect = Rectangle.Empty; return false; } /// public override void Draw() { base.Draw(); var style = Style.Current; var mousePosition = PointFromScreen(Input.MouseScreenPosition); var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey; Render2D.DrawSprite(Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor); if (_arrangeButtonInUse && ArrangeAreaCheck(out _, out var arrangeTargetRect)) { Render2D.FillRectangle(arrangeTargetRect, style.Selection); } } /// public override bool OnMouseDown(Float2 location, MouseButton button) { if (button == MouseButton.Left && _arrangeButtonRect.Contains(ref location)) { _arrangeButtonInUse = true; Focus(); StartMouseCapture(); return true; } return base.OnMouseDown(location, button); } /// public override bool OnMouseUp(Float2 location, MouseButton button) { if (button == MouseButton.Left && _arrangeButtonInUse) { _arrangeButtonInUse = false; EndMouseCapture(); ArrangeAreaCheck(out var index, out _); var action = new ReorderParamAction { OldIndex = (int)Tag, NewIndex = index, Window = _window, Editor = _editor }; action.Do(); _window.Undo.AddAction(action); } return base.OnMouseUp(location, button); } /// public override void OnLostFocus() { if (_arrangeButtonInUse) { _arrangeButtonInUse = false; EndMouseCapture(); } base.OnLostFocus(); } /// protected override void OnSizeChanged() { base.OnSizeChanged(); // Center the drag button vertically _arrangeButtonRect = new Rectangle(2, Mathf.Ceil((Height - 12) * 0.5f) + 1, 12, 12); } } /// /// Custom editor for editing Visject Surface parameters collection. /// /// public class ParametersEditor : CustomEditor { private static readonly Attribute[] DefaultAttributes = { //new LimitAttribute(float.MinValue, float.MaxValue, 0.1f), }; /// /// True if show only public properties, otherwise will display all properties. /// protected bool ShowOnlyPublic = true; /// public override DisplayStyle Style => DisplayStyle.InlineIntoParent; /// public override void Initialize(LayoutElementsContainer layout) { var window = Values[0] as IVisjectSurfaceWindow; var asset = window?.VisjectAsset; if (asset == null) { layout.Label("No parameters"); return; } if (asset.LastLoadFailed) { layout.Label("Failed to load asset"); return; } if (!asset.IsLoaded) { layout.Label("Loading...", TextAlignment.Center); return; } var parameters = window.VisjectSurface.Parameters; CustomEditors.Editors.GenericEditor.OnGroupsBegin(); for (int i = 0; i < parameters.Count; i++) { var p = parameters[i]; if (!p.IsPublic && ShowOnlyPublic) continue; var pIndex = i; var pValue = p.Value; var attributes = p.Meta.GetAttributes(); if (attributes == null || attributes.Length == 0) attributes = DefaultAttributes; var name = p.Name; // Editor Display var editorDisplay = (EditorDisplayAttribute)attributes.FirstOrDefault(x => x is EditorDisplayAttribute); var itemLayout = CustomEditors.Editors.GenericEditor.OnGroup(layout, editorDisplay); if (itemLayout is GroupElement groupElement) groupElement.Panel.Open(false); if (editorDisplay?.Name != null) name = editorDisplay.Name; // Space var space = (SpaceAttribute)attributes.FirstOrDefault(x => x is SpaceAttribute); if (space != null) itemLayout.Space(space.Height); // Header var header = (HeaderAttribute)attributes.FirstOrDefault(x => x is HeaderAttribute); if (header != null) itemLayout.Header(header); var propertyValue = new CustomValueContainer ( p.Type, pValue, (instance, index) => ((IVisjectSurfaceWindow)instance).GetParameter(pIndex), (instance, index, value) => ((IVisjectSurfaceWindow)instance).SetParameter(pIndex, value), attributes ); var propertyLabel = new ParameterPropertyNameLabel(name, this) { Tag = pIndex, Drag = OnDragParameter, }; if (!p.IsPublic) propertyLabel.TextColor = propertyLabel.TextColor.RGBMultiplied(0.7f); var tooltipText = "Type: " + window.VisjectSurface.GetTypeName(p.Type); var tooltip = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); if (tooltip != null) tooltipText += '\n' + tooltip.Text; propertyLabel.MouseLeftDoubleClick += (label, location) => StartParameterRenaming(pIndex, label); propertyLabel.SetupContextMenu += OnPropertyLabelSetupContextMenu; var property = itemLayout.AddPropertyItem(propertyLabel, tooltipText); property.Property("Value", propertyValue); } CustomEditors.Editors.GenericEditor.OnGroupsEnd(); // Parameters creating var newParameterTypes = window.NewParameterTypes; if (newParameterTypes != null) { layout.Space(parameters.Count > 0 ? 10 : 4); var newParam = layout.Button("Add parameter..."); newParam.Button.ButtonClicked += OnAddParameterButtonClicked; layout.Space(10); } } private void OnAddParameterButtonClicked(Button button) { var window = (IVisjectSurfaceWindow)Values[0]; var newParameterTypes = window.NewParameterTypes; // Show context menu with list of parameter types to add var cm = new ItemsListContextMenu(180); foreach (var newParameterType in newParameterTypes) { var item = new TypeSearchPopup.TypeItemView(newParameterType); if (newParameterType.Type != null) item.Name = window.VisjectSurface.GetTypeName(newParameterType); cm.AddItem(item); } cm.ItemClicked += OnAddParameterItemClicked; cm.SortItems(); cm.Show(button.Parent, button.BottomLeft); } private void OnAddParameterItemClicked(ItemsListContextMenu.Item item) { var type = (ScriptType)item.Tag; var window = (IVisjectSurfaceWindow)Values[0]; var asset = window?.VisjectAsset; if (asset == null || !asset.IsLoaded) return; var action = new AddRemoveParamAction { Window = window, IsAdd = true, Name = Utilities.Utils.IncrementNameNumber("New parameter", x => OnParameterRenameValidate(null, x)), Type = type, Index = window.VisjectSurface.Parameters.Count, }; window.VisjectSurface.Undo.AddAction(action); action.Do(); } private DragData OnDragParameter(DraggablePropertyNameLabel label) { var window = (IVisjectSurfaceWindow)Values[0]; var parameter = window.VisjectSurface.Parameters[(int)label.Tag]; return DragNames.GetDragData(SurfaceParameter.DragPrefix, parameter.Name); } private void OnPropertyLabelSetupContextMenu(PropertyNameLabel label, FlaxEditor.GUI.ContextMenu.ContextMenu menu, CustomEditor linkedEditor) { var index = (int)label.Tag; menu.AddSeparator(); menu.AddButton("Rename", () => StartParameterRenaming(index, label)); menu.AddButton("Edit attributes...", () => EditAttributesParameter(index, label)); menu.AddButton("Delete", () => DeleteParameter(index)); OnParamContextMenu(index, menu); } private void StartParameterRenaming(int index, Control label) { var window = (IVisjectSurfaceWindow)Values[0]; var parameter = window.VisjectSurface.Parameters[(int)label.Tag]; var dialog = RenamePopup.Show(label, new Rectangle(0, 0, label.Width - 2, label.Height), parameter.Name, false); dialog.Tag = index; dialog.Validate += OnParameterRenameValidate; dialog.Renamed += OnParameterRenamed; } private bool OnParameterRenameValidate(RenamePopup popup, string value) { var window = (IVisjectSurfaceWindow)Values[0]; return !string.IsNullOrWhiteSpace(value) && window.VisjectSurface.Parameters.All(x => x.Name != value); } private void OnParameterRenamed(RenamePopup renamePopup) { var window = (IVisjectSurfaceWindow)Values[0]; var index = (int)renamePopup.Tag; var action = new RenameParamAction { Window = window, Index = index, Before = window.VisjectSurface.Parameters[index].Name, After = renamePopup.Text, }; window.VisjectSurface.Undo.AddAction(action); action.Do(); } private void EditAttributesParameter(int index, Control label) { var window = (IVisjectSurfaceWindow)Values[0]; var attributes = window.VisjectSurface.Parameters[index].Meta.GetAttributes(); var editor = new AttributesEditor(attributes, NodeFactory.ParameterAttributeTypes); editor.Edited += newValue => { var action = new EditParamAttributesAction { Window = window, Index = index, Before = window.VisjectSurface.Parameters[index].Meta.GetAttributes(), After = newValue, }; window.VisjectSurface.Undo.AddAction(action); action.Do(); }; editor.Show(label, label.Size * 0.5f); } private void DeleteParameter(int index) { var window = (IVisjectSurfaceWindow)Values[0]; var action = new AddRemoveParamAction { Window = window, IsAdd = false, Index = index, }; window.VisjectSurface.Undo.AddAction(action); action.Do(); } /// /// Called to display additional context options for a parameter. /// /// The zero-based parameter index. /// The context menu. protected virtual void OnParamContextMenu(int index, FlaxEditor.GUI.ContextMenu.ContextMenu menu) { menu.AddSeparator(); menu.AddButton("Find references...", () => OnFindReferences(index)); } private void OnFindReferences(int index) { var window = (IVisjectSurfaceWindow)Values[0]; var param = window.VisjectSurface.Parameters[index]; Editor.Instance.ContentFinding.ShowSearch(window.VisjectSurface, '\"' + FlaxEngine.Json.JsonSerializer.GetStringID(param.ID) + '\"'); } } /// /// Dummy class to inject Normal Map parameter type for the material parameter adding picker. /// [Tooltip("Texture asset contains a normal map that is stored on a GPU and is used during rendering graphics to implement normal mapping (aka bump mapping).")] internal sealed class NormalMap { } /// /// The base class for editor windows that use for content editing. /// Note: it uses ClonedAssetEditorWindowBase which is creating cloned asset to edit/preview. /// /// /// /// public abstract class VisjectSurfaceWindow : ClonedAssetEditorWindowBase, IVisjectSurfaceWindow where TAsset : Asset where TSurface : VisjectSurface where TPreview : AssetPreview { /// /// The tab. /// /// /// protected class Tab : FlaxEditor.GUI.Tabs.Tab { /// /// The presenter. /// public CustomEditorPresenter Presenter; /// /// Initializes a new instance of the class. /// /// The tab title text. /// The undo to use for the editing. public Tab(string text, FlaxEditor.Undo undo = null) : base(text) { var scrollPanel = new Panel(ScrollBars.Vertical) { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, Parent = this }; Presenter = new CustomEditorPresenter(undo, "Loading..."); Presenter.Panel.Parent = scrollPanel; } /// public override void OnDestroy() { Presenter.Deselect(); Presenter = null; base.OnDestroy(); } } /// /// The primary split panel. /// protected readonly SplitPanel _split1; /// /// The secondary split panel. /// protected readonly SplitPanel _split2; /// /// The asset preview. /// protected TPreview _preview; /// /// The surface. /// protected TSurface _surface; /// /// The tabs control. Valid only if window is using tabs instead of just properties. /// protected Tabs _tabs; /// /// Save button on a toolstrip. /// protected ToolStripButton _saveButton; /// /// Undo button on a toolstrip. /// protected ToolStripButton _undoButton; /// /// Redo button on a toolstrip. /// protected ToolStripButton _redoButton; private bool _showWholeGraphOnLoad = true; /// /// The properties editor. /// protected CustomEditorPresenter _propertiesEditor; /// /// True if temporary asset is dirty, otherwise false. /// protected bool _tmpAssetIsDirty; /// /// True if window is waiting for asset load to load surface. /// protected bool _isWaitingForSurfaceLoad; /// /// True if window is waiting for asset load to refresh properties editor. /// protected bool _refreshPropertiesOnLoad; /// /// True if parameter value has been changed (special path for handling modifying surface parameters in properties editor). /// protected bool _paramValueChange; /// /// The undo. /// protected FlaxEditor.Undo _undo; /// /// Gets the Visject Surface. /// public TSurface Surface => _surface; /// /// Gets the asset preview. /// public TPreview Preview => _preview; /// /// Gets the undo history context for this window. /// public FlaxEditor.Undo Undo => _undo; /// /// Initializes a new instance of the class. /// /// The editor. /// The item. /// if set to true [use tabs]. protected VisjectSurfaceWindow(Editor editor, AssetItem item, bool useTabs = false) : base(editor, item) { // Undo _undo = new FlaxEditor.Undo(); _undo.UndoDone += OnUndoRedo; _undo.RedoDone += OnUndoRedo; _undo.ActionDone += OnUndoRedo; // Split Panel 1 _split1 = new SplitPanel(Orientation.Horizontal, ScrollBars.None, ScrollBars.None) { AnchorPreset = AnchorPresets.StretchAll, Offsets = new Margin(0, 0, _toolstrip.Bottom, 0), SplitterValue = 0.7f, Parent = this }; // Split Panel 2 _split2 = new SplitPanel(Orientation.Vertical, ScrollBars.None, ScrollBars.Vertical) { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, SplitterValue = 0.4f, Parent = _split1.Panel2 }; // Properties editor if (useTabs) { _tabs = new Tabs { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, TabsSize = new Float2(60, 20), TabsTextHorizontalAlignment = TextAlignment.Center, UseScroll = true, Parent = _split2.Panel2 }; var propertiesTab = new Tab("Properties", _undo); _propertiesEditor = propertiesTab.Presenter; _tabs.AddTab(propertiesTab); } else { _propertiesEditor = new CustomEditorPresenter(_undo, "Loading..."); _propertiesEditor.Panel.Parent = _split2.Panel2; } _propertiesEditor.Modified += OnPropertyEdited; } private void OnUndoRedo(IUndoAction action) { // Hack for emitter properties proxy object if (action is MultiUndoAction multiUndo && multiUndo.Actions.Length == 1 && multiUndo.Actions[0] is UndoActionObject undoActionObject && undoActionObject.Target == _propertiesEditor.Selection[0]) { OnPropertyEdited(); UpdateToolstrip(); return; } _paramValueChange = false; MarkAsEdited(); UpdateToolstrip(); _propertiesEditor.BuildLayoutOnUpdate(); } /// /// Called when the asset properties proxy object gets edited. /// protected virtual void OnPropertyEdited() { _surface.MarkAsEdited(!_paramValueChange); _paramValueChange = false; } /// /// Shows the whole surface graph. /// public void ShowWholeGraph() { _surface.ShowWholeGraph(); } /// /// Refreshes temporary asset to see changes live when editing the surface. /// /// True if cannot refresh it, otherwise false. public bool RefreshTempAsset() { // Early check if (_asset == null || _isWaitingForSurfaceLoad) return true; // Check if surface has been edited if (_surface.IsEdited) { return SaveSurface(); } return false; } /// public override void Save() { if (!IsEdited) return; if (RefreshTempAsset()) { return; } if (SaveToOriginal()) { return; } ClearEditedFlag(); OnSurfaceEditedChanged(); _item.RefreshThumbnail(); } /// protected override void UpdateToolstrip() { _saveButton.Enabled = IsEdited; _undoButton.Enabled = _undo.CanUndo; _redoButton.Enabled = _undo.CanRedo; base.UpdateToolstrip(); } /// protected override void UnlinkItem() { _isWaitingForSurfaceLoad = false; base.UnlinkItem(); } /// protected override void OnAssetLinked() { _isWaitingForSurfaceLoad = true; _refreshPropertiesOnLoad = false; base.OnAssetLinked(); } /// public Asset SurfaceAsset => Asset; /// public abstract string SurfaceName { get; } /// public abstract byte[] SurfaceData { get; set; } /// public VisjectSurfaceContext ParentContext => null; /// public void OnContextCreated(VisjectSurfaceContext context) { } /// public void OnSurfaceEditedChanged() { if (_surface.IsEdited) MarkAsEdited(); } /// public void OnSurfaceGraphEdited() { // Mark as dirty _tmpAssetIsDirty = true; } /// public void OnSurfaceClose() { Close(); } /// /// Called when surface gets loaded and user can edit it. /// protected virtual void OnSurfaceEditingStart() { _undo.Clear(); _surface.Enabled = true; _propertiesEditor.BuildLayout(); } /// /// Loads the surface from the asset. Called during when asset is loaded and surface is missing. /// /// True if failed, otherwise false. protected abstract bool LoadSurface(); /// /// Saves the surface to the asset. Called during when asset is loaded and surface is missing. /// /// True if failed, otherwise false. protected abstract bool SaveSurface(); /// /// Gets a value indicating whether this window can edit asset surface on asset load error (eg. to fix asset loading issue due to graph problem). /// protected virtual bool CanEditSurfaceOnAssetLoadError => false; /// public override void Update(float deltaTime) { base.Update(deltaTime); if (_tmpAssetIsDirty) { _tmpAssetIsDirty = false; RefreshTempAsset(); } if (_isWaitingForSurfaceLoad && (_asset.IsLoaded || (CanEditSurfaceOnAssetLoadError && _asset.LastLoadFailed))) { _isWaitingForSurfaceLoad = false; if (!_asset.IsLoaded) { Editor.LogWarning("Loading surface for asset that is not loaded: " + OriginalAsset); } if (LoadSurface()) { Close(); return; } OnSurfaceEditingStart(); ClearEditedFlag(); if (_showWholeGraphOnLoad) { _showWholeGraphOnLoad = false; _surface.ShowWholeGraph(); } SurfaceLoaded?.Invoke(); } else if (_refreshPropertiesOnLoad && _asset.IsLoaded) { _refreshPropertiesOnLoad = false; _propertiesEditor.BuildLayout(); } } /// public override bool UseLayoutData => true; /// public override void OnLayoutSerialize(XmlWriter writer) { LayoutSerializeSplitter(writer, "Split1", _split1); LayoutSerializeSplitter(writer, "Split2", _split2); } /// public override void OnLayoutDeserialize(XmlElement node) { LayoutDeserializeSplitter(node, "Split1", _split1); LayoutDeserializeSplitter(node, "Split2", _split2); } /// public override void OnLayoutDeserialize() { _split1.SplitterValue = 0.7f; _split2.SplitterValue = 0.4f; } /// public override void OnDestroy() { _undo.Enabled = false; _propertiesEditor.Deselect(); _undo.Clear(); base.OnDestroy(); } /// public abstract IEnumerable NewParameterTypes { get; } /// public event Action SurfaceLoaded; /// public virtual void OnParamRenameUndo() { } /// public virtual void OnParamEditAttributesUndo() { _propertiesEditor.BuildLayout(); } /// public virtual void OnParamAddUndo() { _refreshPropertiesOnLoad = true; } /// public virtual void OnParamRemoveUndo() { _refreshPropertiesOnLoad = true; _propertiesEditor.BuildLayout(); } /// public object GetParameter(int index) { var param = Surface.Parameters[index]; return param.Value; } /// public virtual void SetParameter(int index, object value) { var param = Surface.Parameters[index]; param.Value = value; _paramValueChange = true; } /// public Asset VisjectAsset => Asset; /// public VisjectSurface VisjectSurface => _surface; } }