1087 lines
35 KiB
C#
1087 lines
35 KiB
C#
// Copyright (c) 2012-2023 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;
|
|
|
|
namespace FlaxEditor.Surface
|
|
{
|
|
/// <summary>
|
|
/// The base interface for editor windows that use <see cref="FlaxEditor.Surface.VisjectSurface"/> for content editing.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEditor.Surface.IVisjectSurfaceOwner" />
|
|
interface IVisjectSurfaceWindow : IVisjectSurfaceOwner
|
|
{
|
|
/// <summary>
|
|
/// Gets the asset edited by the window.
|
|
/// </summary>
|
|
Asset VisjectAsset { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the Visject surface editor.
|
|
/// </summary>
|
|
VisjectSurface VisjectSurface { get; }
|
|
|
|
/// <summary>
|
|
/// The new parameter types enum type to use. Null to disable adding new parameters.
|
|
/// </summary>
|
|
IEnumerable<ScriptType> NewParameterTypes { get; }
|
|
|
|
/// <summary>
|
|
/// Event called when surface gets loaded (eg. after opening the window).
|
|
/// </summary>
|
|
event Action SurfaceLoaded;
|
|
|
|
/// <summary>
|
|
/// Called when parameter rename undo action is performed.
|
|
/// </summary>
|
|
void OnParamRenameUndo();
|
|
|
|
/// <summary>
|
|
/// Called when parameter edit attributes undo action is performed.
|
|
/// </summary>
|
|
void OnParamEditAttributesUndo();
|
|
|
|
/// <summary>
|
|
/// Called when parameter add undo action is performed.
|
|
/// </summary>
|
|
void OnParamAddUndo();
|
|
|
|
/// <summary>
|
|
/// Called when parameter remove undo action is performed.
|
|
/// </summary>
|
|
void OnParamRemoveUndo();
|
|
|
|
/// <summary>
|
|
/// Gets the asset parameter.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based parameter index.</param>
|
|
/// <returns>The value.</returns>
|
|
object GetParameter(int index);
|
|
|
|
/// <summary>
|
|
/// Sets the asset parameter.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based parameter index.</param>
|
|
/// <param name="value">The value to set.</param>
|
|
void SetParameter(int index, object value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The surface parameter rename action for undo.
|
|
/// </summary>
|
|
/// <seealso cref="IVisjectSurfaceWindow" />
|
|
sealed class RenameParamAction : IUndoAction
|
|
{
|
|
/// <summary>
|
|
/// The window reference.
|
|
/// </summary>
|
|
public IVisjectSurfaceWindow Window;
|
|
|
|
/// <summary>
|
|
/// The index of the parameter.
|
|
/// </summary>
|
|
public int Index;
|
|
|
|
/// <summary>
|
|
/// The name before.
|
|
/// </summary>
|
|
public string Before;
|
|
|
|
/// <summary>
|
|
/// The name after.
|
|
/// </summary>
|
|
public string After;
|
|
|
|
/// <inheritdoc />
|
|
public string ActionString => "Rename parameter";
|
|
|
|
/// <inheritdoc />
|
|
public void Do()
|
|
{
|
|
Set(After);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
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();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
Window = null;
|
|
Before = null;
|
|
After = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The attributes edit action for undo.
|
|
/// </summary>
|
|
/// <seealso cref="IVisjectSurfaceWindow" />
|
|
abstract class EditAttributesAction : IUndoAction
|
|
{
|
|
/// <summary>
|
|
/// The window reference.
|
|
/// </summary>
|
|
public IVisjectSurfaceWindow Window;
|
|
|
|
/// <summary>
|
|
/// The attributes before.
|
|
/// </summary>
|
|
public Attribute[] Before;
|
|
|
|
/// <summary>
|
|
/// The attributes after.
|
|
/// </summary>
|
|
public Attribute[] After;
|
|
|
|
/// <inheritdoc />
|
|
public string ActionString => "Edit attributes";
|
|
|
|
/// <inheritdoc />
|
|
public void Do()
|
|
{
|
|
Set(After);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Undo()
|
|
{
|
|
Set(Before);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the specified attributes.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
protected abstract void Set(Attribute[] value);
|
|
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
Window = null;
|
|
Before = null;
|
|
After = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The surface attributes edit action for undo.
|
|
/// </summary>
|
|
/// <seealso cref="IVisjectSurfaceWindow" />
|
|
sealed class EditSurfaceAttributesAction : EditAttributesAction
|
|
{
|
|
/// <inheritdoc />
|
|
protected override void Set(Attribute[] value)
|
|
{
|
|
Window.VisjectSurface.Context.Meta.SetAttributes(value);
|
|
Window.VisjectSurface.MarkAsEdited();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The surface node attributes edit action for undo.
|
|
/// </summary>
|
|
/// <seealso cref="IVisjectSurfaceWindow" />
|
|
sealed class EditNodeAttributesAction : EditAttributesAction
|
|
{
|
|
/// <summary>
|
|
/// The id of the node.
|
|
/// </summary>
|
|
public uint ID;
|
|
|
|
/// <inheritdoc />
|
|
protected override void Set(Attribute[] value)
|
|
{
|
|
var node = Window.VisjectSurface.FindNode(ID);
|
|
node.Meta.SetAttributes(value);
|
|
Window.VisjectSurface.MarkAsEdited();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The surface parameter attributes edit action for undo.
|
|
/// </summary>
|
|
/// <seealso cref="IVisjectSurfaceWindow" />
|
|
sealed class EditParamAttributesAction : EditAttributesAction
|
|
{
|
|
/// <summary>
|
|
/// The index of the parameter.
|
|
/// </summary>
|
|
public int Index;
|
|
|
|
/// <inheritdoc />
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The undo action for adding or removing surface parameter.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEditor.Windows.Assets.ClonedAssetEditorWindowBase{TAsset}" />
|
|
/// <seealso cref="FlaxEditor.Surface.IVisjectSurfaceOwner" />
|
|
sealed class AddRemoveParamAction : IUndoAction
|
|
{
|
|
/// <summary>
|
|
/// The window reference.
|
|
/// </summary>
|
|
public IVisjectSurfaceWindow Window;
|
|
|
|
/// <summary>
|
|
/// True if adding, false if removing parameter.
|
|
/// </summary>
|
|
public bool IsAdd;
|
|
|
|
/// <summary>
|
|
/// The index of the parameter.
|
|
/// </summary>
|
|
public int Index;
|
|
|
|
/// <summary>
|
|
/// The name of the parameter.
|
|
/// </summary>
|
|
public string Name;
|
|
|
|
/// <summary>
|
|
/// The type of the parameter.
|
|
/// </summary>
|
|
public ScriptType Type;
|
|
|
|
/// <inheritdoc />
|
|
public string ActionString => IsAdd ? "Add parameter" : "Remove parameter";
|
|
|
|
/// <inheritdoc />
|
|
public void Do()
|
|
{
|
|
if (IsAdd)
|
|
Add();
|
|
else
|
|
Remove();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
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 = SurfaceParameter.Create(type, Name);
|
|
if (IsAdd && Type.Type == typeof(NormalMap))
|
|
param.Value = FlaxEngine.Content.LoadAsyncInternal<Texture>("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();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
Window = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Custom editor for editing Visject Surface parameters collection.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEditor.CustomEditors.CustomEditor" />
|
|
public class ParametersEditor : CustomEditor
|
|
{
|
|
private static readonly Attribute[] DefaultAttributes =
|
|
{
|
|
//new LimitAttribute(float.MinValue, float.MaxValue, 0.1f),
|
|
};
|
|
|
|
/// <summary>
|
|
/// True if show only public properties, otherwise will display all properties.
|
|
/// </summary>
|
|
protected bool ShowOnlyPublic = true;
|
|
|
|
/// <inheritdoc />
|
|
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
|
|
|
|
/// <inheritdoc />
|
|
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 DraggablePropertyNameLabel(name)
|
|
{
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called to display additional context options for a parameter.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based parameter index.</param>
|
|
/// <param name="menu">The context menu.</param>
|
|
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) + '\"');
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dummy class to inject Normal Map parameter type for the material parameter adding picker.
|
|
/// </summary>
|
|
[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
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// The base class for editor windows that use <see cref="FlaxEditor.Surface.VisjectSurface"/> for content editing.
|
|
/// Note: it uses ClonedAssetEditorWindowBase which is creating cloned asset to edit/preview.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
|
/// <seealso cref="FlaxEditor.Surface.IVisjectSurfaceOwner" />
|
|
/// <seealso cref="IVisjectSurfaceWindow" />
|
|
public abstract class VisjectSurfaceWindow<TAsset, TSurface, TPreview> : ClonedAssetEditorWindowBase<TAsset>, IVisjectSurfaceWindow
|
|
where TAsset : Asset
|
|
where TSurface : VisjectSurface
|
|
where TPreview : AssetPreview
|
|
{
|
|
/// <summary>
|
|
/// The tab.
|
|
/// </summary>
|
|
/// <seealso cref="FlaxEditor.Windows.Assets.ClonedAssetEditorWindowBase{TAsset}" />
|
|
/// <seealso cref="FlaxEditor.Surface.IVisjectSurfaceWindow" />
|
|
protected class Tab : FlaxEditor.GUI.Tabs.Tab
|
|
{
|
|
/// <summary>
|
|
/// The presenter.
|
|
/// </summary>
|
|
public CustomEditorPresenter Presenter;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Tab"/> class.
|
|
/// </summary>
|
|
/// <param name="text">The tab title text.</param>
|
|
/// <param name="undo">The undo to use for the editing.</param>
|
|
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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnDestroy()
|
|
{
|
|
Presenter.Deselect();
|
|
Presenter = null;
|
|
|
|
base.OnDestroy();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The primary split panel.
|
|
/// </summary>
|
|
protected readonly SplitPanel _split1;
|
|
|
|
/// <summary>
|
|
/// The secondary split panel.
|
|
/// </summary>
|
|
protected readonly SplitPanel _split2;
|
|
|
|
/// <summary>
|
|
/// The asset preview.
|
|
/// </summary>
|
|
protected TPreview _preview;
|
|
|
|
/// <summary>
|
|
/// The surface.
|
|
/// </summary>
|
|
protected TSurface _surface;
|
|
|
|
/// <summary>
|
|
/// The tabs control. Valid only if window is using tabs instead of just properties.
|
|
/// </summary>
|
|
protected Tabs _tabs;
|
|
|
|
private readonly ToolStripButton _saveButton;
|
|
private readonly ToolStripButton _undoButton;
|
|
private readonly ToolStripButton _redoButton;
|
|
private bool _showWholeGraphOnLoad = true;
|
|
|
|
/// <summary>
|
|
/// The properties editor.
|
|
/// </summary>
|
|
protected CustomEditorPresenter _propertiesEditor;
|
|
|
|
/// <summary>
|
|
/// True if temporary asset is dirty, otherwise false.
|
|
/// </summary>
|
|
protected bool _tmpAssetIsDirty;
|
|
|
|
/// <summary>
|
|
/// True if window is waiting for asset load to load surface.
|
|
/// </summary>
|
|
protected bool _isWaitingForSurfaceLoad;
|
|
|
|
/// <summary>
|
|
/// True if window is waiting for asset load to refresh properties editor.
|
|
/// </summary>
|
|
protected bool _refreshPropertiesOnLoad;
|
|
|
|
/// <summary>
|
|
/// True if parameter value has been changed (special path for handling modifying surface parameters in properties editor).
|
|
/// </summary>
|
|
protected bool _paramValueChange;
|
|
|
|
/// <summary>
|
|
/// The undo.
|
|
/// </summary>
|
|
protected FlaxEditor.Undo _undo;
|
|
|
|
/// <summary>
|
|
/// Gets the Visject Surface.
|
|
/// </summary>
|
|
public TSurface Surface => _surface;
|
|
|
|
/// <summary>
|
|
/// Gets the asset preview.
|
|
/// </summary>
|
|
public TPreview Preview => _preview;
|
|
|
|
/// <summary>
|
|
/// Gets the undo history context for this window.
|
|
/// </summary>
|
|
public FlaxEditor.Undo Undo => _undo;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="VisjectSurfaceWindow{TAsset, TSurface, TPreview}"/> class.
|
|
/// </summary>
|
|
/// <param name="editor">The editor.</param>
|
|
/// <param name="item">The item.</param>
|
|
/// <param name="useTabs">if set to <c>true</c> [use tabs].</param>
|
|
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;
|
|
|
|
// Toolstrip
|
|
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
|
_toolstrip.AddSeparator();
|
|
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
|
|
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
|
|
_toolstrip.AddSeparator();
|
|
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip("Open content search tool (Ctrl+F)");
|
|
_toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
|
|
|
|
// Setup input actions
|
|
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
|
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
|
InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the asset properties proxy object gets edited.
|
|
/// </summary>
|
|
protected virtual void OnPropertyEdited()
|
|
{
|
|
_surface.MarkAsEdited(!_paramValueChange);
|
|
_paramValueChange = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shows the whole surface graph.
|
|
/// </summary>
|
|
public void ShowWholeGraph()
|
|
{
|
|
_surface.ShowWholeGraph();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refreshes temporary asset to see changes live when editing the surface.
|
|
/// </summary>
|
|
/// <returns>True if cannot refresh it, otherwise false.</returns>
|
|
public bool RefreshTempAsset()
|
|
{
|
|
// Early check
|
|
if (_asset == null || _isWaitingForSurfaceLoad)
|
|
return true;
|
|
|
|
// Check if surface has been edited
|
|
if (_surface.IsEdited)
|
|
{
|
|
return SaveSurface();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void Save()
|
|
{
|
|
if (!IsEdited)
|
|
return;
|
|
|
|
if (RefreshTempAsset())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (SaveToOriginal())
|
|
{
|
|
return;
|
|
}
|
|
|
|
ClearEditedFlag();
|
|
OnSurfaceEditedChanged();
|
|
_item.RefreshThumbnail();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void UpdateToolstrip()
|
|
{
|
|
_saveButton.Enabled = IsEdited;
|
|
_undoButton.Enabled = _undo.CanUndo;
|
|
_redoButton.Enabled = _undo.CanRedo;
|
|
|
|
base.UpdateToolstrip();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void UnlinkItem()
|
|
{
|
|
_isWaitingForSurfaceLoad = false;
|
|
|
|
base.UnlinkItem();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void OnAssetLinked()
|
|
{
|
|
_isWaitingForSurfaceLoad = true;
|
|
_refreshPropertiesOnLoad = false;
|
|
|
|
base.OnAssetLinked();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Asset SurfaceAsset => Asset;
|
|
|
|
/// <inheritdoc />
|
|
public abstract string SurfaceName { get; }
|
|
|
|
/// <inheritdoc />
|
|
public abstract byte[] SurfaceData { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public VisjectSurfaceContext ParentContext => null;
|
|
|
|
/// <inheritdoc />
|
|
public void OnContextCreated(VisjectSurfaceContext context)
|
|
{
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnSurfaceEditedChanged()
|
|
{
|
|
if (_surface.IsEdited)
|
|
MarkAsEdited();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnSurfaceGraphEdited()
|
|
{
|
|
// Mark as dirty
|
|
_tmpAssetIsDirty = true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void OnSurfaceClose()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when surface gets loaded and user can edit it.
|
|
/// </summary>
|
|
protected virtual void OnSurfaceEditingStart()
|
|
{
|
|
_undo.Clear();
|
|
_surface.Enabled = true;
|
|
_propertiesEditor.BuildLayout();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads the surface from the asset. Called during <see cref="Update"/> when asset is loaded and surface is missing.
|
|
/// </summary>
|
|
/// <returns>True if failed, otherwise false.</returns>
|
|
protected abstract bool LoadSurface();
|
|
|
|
/// <summary>
|
|
/// Saves the surface to the asset. Called during <see cref="Update"/> when asset is loaded and surface is missing.
|
|
/// </summary>
|
|
/// <returns>True if failed, otherwise false.</returns>
|
|
protected abstract bool SaveSurface();
|
|
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
protected virtual bool CanEditSurfaceOnAssetLoadError => false;
|
|
|
|
/// <inheritdoc />
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool UseLayoutData => true;
|
|
|
|
/// <inheritdoc />
|
|
public override void OnLayoutSerialize(XmlWriter writer)
|
|
{
|
|
LayoutSerializeSplitter(writer, "Split1", _split1);
|
|
LayoutSerializeSplitter(writer, "Split2", _split2);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnLayoutDeserialize(XmlElement node)
|
|
{
|
|
LayoutDeserializeSplitter(node, "Split1", _split1);
|
|
LayoutDeserializeSplitter(node, "Split2", _split2);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnLayoutDeserialize()
|
|
{
|
|
_split1.SplitterValue = 0.7f;
|
|
_split2.SplitterValue = 0.4f;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void OnDestroy()
|
|
{
|
|
_undo.Enabled = false;
|
|
_propertiesEditor.Deselect();
|
|
_undo.Clear();
|
|
|
|
base.OnDestroy();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public abstract IEnumerable<ScriptType> NewParameterTypes { get; }
|
|
|
|
/// <inheritdoc />
|
|
public event Action SurfaceLoaded;
|
|
|
|
/// <inheritdoc />
|
|
public virtual void OnParamRenameUndo()
|
|
{
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public virtual void OnParamEditAttributesUndo()
|
|
{
|
|
_propertiesEditor.BuildLayout();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public virtual void OnParamAddUndo()
|
|
{
|
|
_refreshPropertiesOnLoad = true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public virtual void OnParamRemoveUndo()
|
|
{
|
|
_refreshPropertiesOnLoad = true;
|
|
//_propertiesEditor.BuildLayoutOnUpdate();
|
|
_propertiesEditor.BuildLayout();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public object GetParameter(int index)
|
|
{
|
|
var param = Surface.Parameters[index];
|
|
return param.Value;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public virtual void SetParameter(int index, object value)
|
|
{
|
|
var param = Surface.Parameters[index];
|
|
param.Value = value;
|
|
_paramValueChange = true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Asset VisjectAsset => Asset;
|
|
|
|
/// <inheritdoc />
|
|
public VisjectSurface VisjectSurface => _surface;
|
|
}
|
|
}
|