testss
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using ContextualMenuManipulator = UnityEngine.UIElements.ContextualMenuManipulator;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views.Blackboard
|
||||
{
|
||||
class BlackboardFieldView : BlackboardField, IInspectable
|
||||
{
|
||||
readonly GraphData m_Graph;
|
||||
public GraphData graph => m_Graph;
|
||||
|
||||
ShaderInput m_Input;
|
||||
|
||||
[Inspectable("Shader Input", null)]
|
||||
public ShaderInput shaderInput => m_Input;
|
||||
|
||||
private void DirtyNodes(ModificationScope modificationScope = ModificationScope.Node)
|
||||
{
|
||||
switch (m_Input)
|
||||
{
|
||||
case AbstractShaderProperty property:
|
||||
var graphEditorView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
if (graphEditorView == null)
|
||||
return;
|
||||
var colorManager = graphEditorView.colorManager;
|
||||
var nodes = graphEditorView.graphView.Query<MaterialNodeView>().ToList();
|
||||
|
||||
colorManager.SetNodesDirty(nodes);
|
||||
colorManager.UpdateNodeViews(nodes);
|
||||
|
||||
foreach (var node in graph.GetNodes<PropertyNode>())
|
||||
{
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
break;
|
||||
case ShaderKeyword keyword:
|
||||
foreach (var node in graph.GetNodes<KeywordNode>())
|
||||
{
|
||||
node.UpdateNode();
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
|
||||
// Cant determine if Sub Graphs contain the keyword so just update them
|
||||
foreach (var node in graph.GetNodes<SubGraphNode>())
|
||||
{
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
// When the properties are changed, these delegates are used to trigger an update in the other views that also represent those properties
|
||||
private Action m_inspectorUpdateTrigger;
|
||||
private Action m_ResetReferenceNameAction;
|
||||
Label m_NameLabelField;
|
||||
|
||||
public string inspectorTitle
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (m_Input)
|
||||
{
|
||||
case AbstractShaderProperty property:
|
||||
return $"{m_Input.displayName} (Property)";
|
||||
case ShaderKeyword keyword:
|
||||
return $"{m_Input.displayName} (Keyword)";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InspectorUpdateTrigger()
|
||||
{
|
||||
if (m_inspectorUpdateTrigger != null)
|
||||
m_inspectorUpdateTrigger();
|
||||
}
|
||||
|
||||
private void UpdateTypeText()
|
||||
{
|
||||
if (shaderInput is AbstractShaderProperty asp)
|
||||
{
|
||||
typeText = asp.GetPropertyTypeString();
|
||||
}
|
||||
}
|
||||
|
||||
public BlackboardFieldView(GraphData graph,
|
||||
ShaderInput input,
|
||||
Texture icon,
|
||||
string text,
|
||||
string typeText) : base(icon, text, typeText)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/ShaderGraphBlackboard"));
|
||||
m_Graph = graph;
|
||||
m_Input = input;
|
||||
this.name = "blackboardFieldView";
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged += UpdateTypeText;
|
||||
|
||||
// add the right click context menu
|
||||
IManipulator contextMenuManipulator = new ContextualMenuManipulator(AddContextMenuOptions);
|
||||
this.AddManipulator(contextMenuManipulator);
|
||||
|
||||
var nameTextField = this.Q("textField") as TextField;
|
||||
var textinput = nameTextField.Q(TextField.textInputUssName);
|
||||
// When a display name is changed through the BlackboardPill, this callback handle it
|
||||
textinput.RegisterCallback<FocusOutEvent>(e =>
|
||||
{
|
||||
this.RegisterPropertyChangeUndo("Change Display Name");
|
||||
|
||||
if (nameTextField.text != m_Input.displayName)
|
||||
m_Input.SetDisplayNameAndSanitizeForGraph(m_Graph, nameTextField.text);
|
||||
|
||||
// This gets triggered on property creation so need to check for inspector update trigger being valid (which it might not be at the time)
|
||||
if (this.m_inspectorUpdateTrigger != null)
|
||||
this.MarkNodesAsDirty(true, ModificationScope.Topological);
|
||||
else
|
||||
DirtyNodes(ModificationScope.Topological);
|
||||
});
|
||||
|
||||
m_NameLabelField = this.Q("title-label") as Label;
|
||||
|
||||
// Set callback association for display name updates
|
||||
m_Input.displayNameUpdateTrigger += UpdateDisplayNameText;
|
||||
}
|
||||
|
||||
~BlackboardFieldView()
|
||||
{
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged -= UpdateTypeText;
|
||||
}
|
||||
|
||||
public object GetObjectToInspect()
|
||||
{
|
||||
return shaderInput;
|
||||
}
|
||||
|
||||
void AddContextMenuOptions(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
// Checks if the reference name has been overridden and appends menu action to reset it, if so
|
||||
if (m_Input.isRenamable &&
|
||||
!string.IsNullOrEmpty(m_Input.overrideReferenceName))
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Reset Reference",
|
||||
e =>
|
||||
{
|
||||
m_ResetReferenceNameAction?.Invoke();
|
||||
DirtyNodes(ModificationScope.Graph);
|
||||
},
|
||||
DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
#region PropertyDrawers
|
||||
public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate)
|
||||
{
|
||||
if (propertyDrawer is ShaderInputPropertyDrawer shaderInputPropertyDrawer)
|
||||
{
|
||||
shaderInputPropertyDrawer.GetPropertyData(
|
||||
m_Graph.isSubGraph,
|
||||
m_Graph,
|
||||
ChangeExposedField,
|
||||
() => m_Graph.ValidateGraph(),
|
||||
() => m_Graph.OnKeywordChanged(),
|
||||
ChangePropertyValue,
|
||||
RegisterPropertyChangeUndo,
|
||||
MarkNodesAsDirty);
|
||||
|
||||
m_inspectorUpdateTrigger = inspectorUpdateDelegate;
|
||||
m_ResetReferenceNameAction = shaderInputPropertyDrawer.ResetReferenceName;
|
||||
|
||||
this.RegisterCallback<DetachFromPanelEvent>(evt => m_inspectorUpdateTrigger());
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeExposedField(bool newValue)
|
||||
{
|
||||
m_Input.generatePropertyBlock = newValue;
|
||||
icon = (m_Graph.isSubGraph || m_Input.isExposed) ? BlackboardProvider.exposedIcon : null;
|
||||
}
|
||||
|
||||
void UpdateDisplayNameText(string newDisplayName)
|
||||
{
|
||||
m_NameLabelField.text = newDisplayName;
|
||||
}
|
||||
|
||||
void RegisterPropertyChangeUndo(string actionName)
|
||||
{
|
||||
m_Graph.owner.RegisterCompleteObjectUndo(actionName);
|
||||
}
|
||||
|
||||
void MarkNodesAsDirty(bool triggerPropertyViewUpdate = false, ModificationScope modificationScope = ModificationScope.Node)
|
||||
{
|
||||
DirtyNodes(modificationScope);
|
||||
if (triggerPropertyViewUpdate)
|
||||
m_inspectorUpdateTrigger();
|
||||
}
|
||||
|
||||
void ChangePropertyValue(object newValue)
|
||||
{
|
||||
var property = m_Input as AbstractShaderProperty;
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
switch (property)
|
||||
{
|
||||
case BooleanShaderProperty booleanProperty:
|
||||
booleanProperty.value = ((ToggleData)newValue).isOn;
|
||||
break;
|
||||
case Vector1ShaderProperty vector1Property:
|
||||
vector1Property.value = (float)newValue;
|
||||
break;
|
||||
case Vector2ShaderProperty vector2Property:
|
||||
vector2Property.value = (Vector2)newValue;
|
||||
break;
|
||||
case Vector3ShaderProperty vector3Property:
|
||||
vector3Property.value = (Vector3)newValue;
|
||||
break;
|
||||
case Vector4ShaderProperty vector4Property:
|
||||
vector4Property.value = (Vector4)newValue;
|
||||
break;
|
||||
case ColorShaderProperty colorProperty:
|
||||
colorProperty.value = (Color)newValue;
|
||||
break;
|
||||
case Texture2DShaderProperty texture2DProperty:
|
||||
texture2DProperty.value.texture = (Texture)newValue;
|
||||
break;
|
||||
case Texture2DArrayShaderProperty texture2DArrayProperty:
|
||||
texture2DArrayProperty.value.textureArray = (Texture2DArray)newValue;
|
||||
break;
|
||||
case Texture3DShaderProperty texture3DProperty:
|
||||
texture3DProperty.value.texture = (Texture3D)newValue;
|
||||
break;
|
||||
case CubemapShaderProperty cubemapProperty:
|
||||
cubemapProperty.value.cubemap = (Cubemap)newValue;
|
||||
break;
|
||||
case Matrix2ShaderProperty matrix2Property:
|
||||
matrix2Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case Matrix3ShaderProperty matrix3Property:
|
||||
matrix3Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case Matrix4ShaderProperty matrix4Property:
|
||||
matrix4Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case SamplerStateShaderProperty samplerStateProperty:
|
||||
samplerStateProperty.value = (TextureSamplerState)newValue;
|
||||
break;
|
||||
case GradientShaderProperty gradientProperty:
|
||||
gradientProperty.value = (Gradient)newValue;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
class BlackboardInputInfo : Attribute
|
||||
{
|
||||
public float priority;
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
/// Provide additional information to provide the blackboard for order and name of the ShaderInput item.
|
||||
/// </summary>
|
||||
/// <param name="priority">Priority of the item, higher values will result in lower positions in the menu.</param>
|
||||
/// <param name="name">Name of the item. If null, the class name of the item will be used instead.</param>
|
||||
public BlackboardInputInfo(float priority, string name = null)
|
||||
{
|
||||
this.priority = priority;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,500 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views.Blackboard
|
||||
{
|
||||
class BlackboardProvider
|
||||
{
|
||||
readonly GraphData m_Graph;
|
||||
public static readonly Texture2D exposedIcon = Resources.Load<Texture2D>("GraphView/Nodes/BlackboardFieldExposed");
|
||||
readonly Dictionary<ShaderInput, BlackboardRow> m_InputRows;
|
||||
readonly SGBlackboardSection m_PropertySection;
|
||||
readonly SGBlackboardSection m_KeywordSection;
|
||||
|
||||
public const int k_PropertySectionIndex = 0;
|
||||
public const int k_KeywordSectionIndex = 1;
|
||||
const string k_styleName = "Blackboard";
|
||||
|
||||
public SGBlackboard blackboard { get; private set; }
|
||||
Label m_PathLabel;
|
||||
TextField m_PathLabelTextField;
|
||||
bool m_EditPathCancelled = false;
|
||||
List<Node> m_SelectedNodes = new List<Node>();
|
||||
|
||||
public string assetName
|
||||
{
|
||||
get { return blackboard.title; }
|
||||
set
|
||||
{
|
||||
blackboard.title = value;
|
||||
}
|
||||
}
|
||||
|
||||
public BlackboardProvider(GraphData graph, GraphView associatedGraphView)
|
||||
{
|
||||
m_Graph = graph;
|
||||
m_InputRows = new Dictionary<ShaderInput, BlackboardRow>();
|
||||
|
||||
blackboard = new SGBlackboard(associatedGraphView)
|
||||
{
|
||||
subTitle = FormatPath(graph.path),
|
||||
addItemRequested = AddItemRequested,
|
||||
moveItemRequested = MoveItemRequested
|
||||
};
|
||||
|
||||
// These make sure that the drag indicators are disabled whenever a drag action is cancelled without completing a drop
|
||||
blackboard.RegisterCallback<MouseUpEvent>(evt =>
|
||||
{
|
||||
m_PropertySection.OnDragActionCanceled();
|
||||
m_KeywordSection.OnDragActionCanceled();
|
||||
});
|
||||
|
||||
blackboard.RegisterCallback<DragExitedEvent>(evt =>
|
||||
{
|
||||
m_PropertySection.OnDragActionCanceled();
|
||||
m_KeywordSection.OnDragActionCanceled();
|
||||
});
|
||||
|
||||
|
||||
m_PathLabel = blackboard.hierarchy.ElementAt(0).Q<Label>("subTitleLabel");
|
||||
m_PathLabel.RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
|
||||
|
||||
m_PathLabelTextField = new TextField { visible = false };
|
||||
m_PathLabelTextField.Q("unity-text-input").RegisterCallback<FocusOutEvent>(e => { OnEditPathTextFinished(); });
|
||||
m_PathLabelTextField.Q("unity-text-input").RegisterCallback<KeyDownEvent>(OnPathTextFieldKeyPressed);
|
||||
blackboard.hierarchy.Add(m_PathLabelTextField);
|
||||
|
||||
m_PropertySection = new SGBlackboardSection { title = "Properties" };
|
||||
foreach (var property in graph.properties)
|
||||
AddInputRow(property);
|
||||
blackboard.Add(m_PropertySection);
|
||||
|
||||
m_KeywordSection = new SGBlackboardSection { title = "Keywords" };
|
||||
foreach (var keyword in graph.keywords)
|
||||
AddInputRow(keyword);
|
||||
blackboard.Add(m_KeywordSection);
|
||||
}
|
||||
|
||||
void OnDragUpdatedEvent(DragUpdatedEvent evt)
|
||||
{
|
||||
if (m_SelectedNodes.Any())
|
||||
{
|
||||
foreach (var node in m_SelectedNodes)
|
||||
{
|
||||
node.RemoveFromClassList("hovered");
|
||||
}
|
||||
m_SelectedNodes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseDownEvent(MouseDownEvent evt)
|
||||
{
|
||||
if (evt.clickCount == 2 && evt.button == (int)MouseButton.LeftMouse)
|
||||
{
|
||||
StartEditingPath();
|
||||
evt.PreventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
void StartEditingPath()
|
||||
{
|
||||
m_PathLabelTextField.visible = true;
|
||||
|
||||
m_PathLabelTextField.value = m_PathLabel.text;
|
||||
m_PathLabelTextField.style.position = Position.Absolute;
|
||||
var rect = m_PathLabel.ChangeCoordinatesTo(blackboard, new Rect(Vector2.zero, m_PathLabel.layout.size));
|
||||
m_PathLabelTextField.style.left = rect.xMin;
|
||||
m_PathLabelTextField.style.top = rect.yMin;
|
||||
m_PathLabelTextField.style.width = rect.width;
|
||||
m_PathLabelTextField.style.fontSize = 11;
|
||||
m_PathLabelTextField.style.marginLeft = 0;
|
||||
m_PathLabelTextField.style.marginRight = 0;
|
||||
m_PathLabelTextField.style.marginTop = 0;
|
||||
m_PathLabelTextField.style.marginBottom = 0;
|
||||
|
||||
m_PathLabel.visible = false;
|
||||
|
||||
m_PathLabelTextField.Q("unity-text-input").Focus();
|
||||
m_PathLabelTextField.SelectAll();
|
||||
}
|
||||
|
||||
void OnPathTextFieldKeyPressed(KeyDownEvent evt)
|
||||
{
|
||||
switch (evt.keyCode)
|
||||
{
|
||||
case KeyCode.Escape:
|
||||
m_EditPathCancelled = true;
|
||||
m_PathLabelTextField.Q("unity-text-input").Blur();
|
||||
break;
|
||||
case KeyCode.Return:
|
||||
case KeyCode.KeypadEnter:
|
||||
m_PathLabelTextField.Q("unity-text-input").Blur();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnEditPathTextFinished()
|
||||
{
|
||||
m_PathLabel.visible = true;
|
||||
m_PathLabelTextField.visible = false;
|
||||
|
||||
var newPath = m_PathLabelTextField.text;
|
||||
if (!m_EditPathCancelled && (newPath != m_PathLabel.text))
|
||||
{
|
||||
newPath = SanitizePath(newPath);
|
||||
}
|
||||
|
||||
m_Graph.path = newPath;
|
||||
m_PathLabel.text = FormatPath(newPath);
|
||||
m_EditPathCancelled = false;
|
||||
}
|
||||
|
||||
static string FormatPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return "—";
|
||||
return path;
|
||||
}
|
||||
|
||||
static string SanitizePath(string path)
|
||||
{
|
||||
var splitString = path.Split('/');
|
||||
List<string> newStrings = new List<string>();
|
||||
foreach (string s in splitString)
|
||||
{
|
||||
var str = s.Trim();
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
{
|
||||
newStrings.Add(str);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("/", newStrings.ToArray());
|
||||
}
|
||||
|
||||
void MoveItemRequested(SGBlackboard blackboard, int newIndex, VisualElement visualElement)
|
||||
{
|
||||
var input = visualElement.userData as ShaderInput;
|
||||
if (input == null)
|
||||
return;
|
||||
|
||||
m_Graph.owner.RegisterCompleteObjectUndo("Move Graph Input");
|
||||
switch (input)
|
||||
{
|
||||
case AbstractShaderProperty property:
|
||||
m_Graph.MoveProperty(property, newIndex);
|
||||
break;
|
||||
case ShaderKeyword keyword:
|
||||
m_Graph.MoveKeyword(keyword, newIndex);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
void AddItemRequested(SGBlackboard blackboard)
|
||||
{
|
||||
var gm = new GenericMenu();
|
||||
AddPropertyItems(gm);
|
||||
AddKeywordItems(gm);
|
||||
gm.ShowAsContext();
|
||||
}
|
||||
|
||||
void AddPropertyItems(GenericMenu gm)
|
||||
{
|
||||
var shaderInputTypes = TypeCache.GetTypesWithAttribute<BlackboardInputInfo>().ToList();
|
||||
|
||||
// Sort the ShaderInput by priority using the BlackboardInputInfo attribute
|
||||
shaderInputTypes.Sort((s1, s2) => {
|
||||
var info1 = Attribute.GetCustomAttribute(s1, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
|
||||
var info2 = Attribute.GetCustomAttribute(s2, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
|
||||
|
||||
if (info1.priority == info2.priority)
|
||||
return (info1.name ?? s1.Name).CompareTo(info2.name ?? s2.Name);
|
||||
else
|
||||
return info1.priority.CompareTo(info2.priority);
|
||||
});
|
||||
|
||||
foreach (var t in shaderInputTypes)
|
||||
{
|
||||
if (t.IsAbstract)
|
||||
continue;
|
||||
|
||||
var info = Attribute.GetCustomAttribute(t, typeof(BlackboardInputInfo)) as BlackboardInputInfo;
|
||||
string name = info?.name ?? ObjectNames.NicifyVariableName(t.Name.Replace("ShaderProperty", ""));
|
||||
ShaderInput si = Activator.CreateInstance(t, true) as ShaderInput;
|
||||
gm.AddItem(new GUIContent(name), false, () => AddInputRow(si, true));
|
||||
//QUICK FIX TO DEAL WITH DEPRECATED COLOR PROPERTY
|
||||
if (ShaderGraphPreferences.allowDeprecatedBehaviors && si is ColorShaderProperty csp)
|
||||
{
|
||||
gm.AddItem(new GUIContent($"Color (Deprecated)"), false, () => AddInputRow(new ColorShaderProperty(ColorShaderProperty.deprecatedVersion), true));
|
||||
}
|
||||
}
|
||||
gm.AddSeparator($"/");
|
||||
}
|
||||
|
||||
void AddKeywordItems(GenericMenu gm)
|
||||
{
|
||||
gm.AddItem(new GUIContent($"Keyword/Boolean"), false, () => AddInputRow(new ShaderKeyword(KeywordType.Boolean), true));
|
||||
gm.AddItem(new GUIContent($"Keyword/Enum"), false, () => AddInputRow(new ShaderKeyword(KeywordType.Enum), true));
|
||||
gm.AddSeparator($"Keyword/");
|
||||
foreach (var builtinKeywordDescriptor in KeywordUtil.GetBuiltinKeywordDescriptors())
|
||||
{
|
||||
var keyword = ShaderKeyword.CreateBuiltInKeyword(builtinKeywordDescriptor);
|
||||
AddBuiltinKeyword(gm, keyword);
|
||||
}
|
||||
}
|
||||
|
||||
void AddBuiltinKeyword(GenericMenu gm, ShaderKeyword keyword)
|
||||
{
|
||||
if (m_Graph.keywords.Where(x => x.referenceName == keyword.referenceName).Any())
|
||||
{
|
||||
gm.AddDisabledItem(new GUIContent($"Keyword/{keyword.displayName}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
gm.AddItem(new GUIContent($"Keyword/{keyword.displayName}"), false, () => AddInputRow(m_Graph.AddCopyOfShaderInput(keyword)));
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleGraphChanges(bool wasUndoRedoPerformed)
|
||||
{
|
||||
var selection = new List<ISelectable>();
|
||||
if (blackboard.selection != null)
|
||||
{
|
||||
selection.AddRange(blackboard.selection);
|
||||
}
|
||||
|
||||
foreach (var shaderInput in m_Graph.removedInputs)
|
||||
{
|
||||
BlackboardRow row;
|
||||
if (m_InputRows.TryGetValue(shaderInput, out row))
|
||||
{
|
||||
row.RemoveFromHierarchy();
|
||||
m_InputRows.Remove(shaderInput);
|
||||
}
|
||||
}
|
||||
|
||||
// This tries to maintain the selection the user had before the undo/redo was performed,
|
||||
// if the user hasn't added or removed any inputs
|
||||
if (wasUndoRedoPerformed)
|
||||
{
|
||||
oldSelectionPersistenceData.Clear();
|
||||
foreach (var item in selection)
|
||||
{
|
||||
if (item is BlackboardFieldView blackboardFieldView)
|
||||
{
|
||||
var guid = blackboardFieldView.shaderInput.referenceName;
|
||||
oldSelectionPersistenceData.Add(guid, blackboardFieldView.viewDataKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var input in m_Graph.addedInputs)
|
||||
{
|
||||
AddInputRow(input, index: m_Graph.GetGraphInputIndex(input));
|
||||
}
|
||||
|
||||
if (m_Graph.movedInputs.Any())
|
||||
{
|
||||
foreach (var row in m_InputRows.Values)
|
||||
row.RemoveFromHierarchy();
|
||||
|
||||
foreach (var property in m_Graph.properties)
|
||||
m_PropertySection.Add(m_InputRows[property]);
|
||||
|
||||
foreach (var keyword in m_Graph.keywords)
|
||||
m_KeywordSection.Add(m_InputRows[keyword]);
|
||||
}
|
||||
}
|
||||
|
||||
// A map from shaderInput reference names to the viewDataKey of the blackboardFieldView that used to represent them
|
||||
// This data is used to re-select the shaderInputs in the blackboard after an undo/redo is performed
|
||||
Dictionary<string, string> oldSelectionPersistenceData { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
void AddInputRow(ShaderInput input, bool addToGraph = false, int index = -1)
|
||||
{
|
||||
if (m_InputRows.ContainsKey(input))
|
||||
return;
|
||||
|
||||
if (addToGraph)
|
||||
{
|
||||
m_Graph.owner.RegisterCompleteObjectUndo("Create Graph Input");
|
||||
|
||||
// this pathway is mostly used for adding newly inputs to the graph
|
||||
// so this is setting up the default state for those inputs
|
||||
// here we flag it exposed, if the input type is exposable
|
||||
input.generatePropertyBlock = input.isExposable;
|
||||
|
||||
m_Graph.AddGraphInput(input); // TODO: index after currently selected property
|
||||
}
|
||||
|
||||
BlackboardFieldView field = null;
|
||||
BlackboardRow row = null;
|
||||
|
||||
switch (input)
|
||||
{
|
||||
case AbstractShaderProperty property:
|
||||
{
|
||||
var icon = (m_Graph.isSubGraph || property.isExposed) ? exposedIcon : null;
|
||||
field = new BlackboardFieldView(m_Graph, property, icon, property.displayName, property.GetPropertyTypeString()) { userData = property };
|
||||
field.RegisterCallback<AttachToPanelEvent>(UpdateSelectionAfterUndoRedo);
|
||||
property.onBeforeVersionChange += (_) => m_Graph.owner.RegisterCompleteObjectUndo($"Change {property.displayName} Version");
|
||||
void UpdateField()
|
||||
{
|
||||
field.typeText = property.GetPropertyTypeString();
|
||||
field.InspectorUpdateTrigger();
|
||||
}
|
||||
|
||||
property.onAfterVersionChange += UpdateField;
|
||||
row = new BlackboardRow(field, null);
|
||||
|
||||
if (index < 0 || index > m_InputRows.Count)
|
||||
index = m_InputRows.Count;
|
||||
|
||||
if (index == m_InputRows.Count)
|
||||
m_PropertySection.Add(row);
|
||||
else
|
||||
m_PropertySection.Insert(index, row);
|
||||
|
||||
break;
|
||||
}
|
||||
case ShaderKeyword keyword:
|
||||
{
|
||||
var icon = (m_Graph.isSubGraph || keyword.isExposed) ? exposedIcon : null;
|
||||
|
||||
string typeText = keyword.keywordType.ToString() + " Keyword";
|
||||
typeText = keyword.isBuiltIn ? "Built-in " + typeText : typeText;
|
||||
|
||||
field = new BlackboardFieldView(m_Graph, keyword, icon, keyword.displayName, typeText) { userData = keyword };
|
||||
field.RegisterCallback<AttachToPanelEvent>(UpdateSelectionAfterUndoRedo);
|
||||
row = new BlackboardRow(field, null);
|
||||
|
||||
if (index < 0 || index > m_InputRows.Count)
|
||||
index = m_InputRows.Count;
|
||||
|
||||
if (index == m_InputRows.Count)
|
||||
m_KeywordSection.Add(row);
|
||||
else
|
||||
m_KeywordSection.Insert(index, row);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
field.RegisterCallback<MouseEnterEvent>(evt => OnMouseHover(evt, input));
|
||||
field.RegisterCallback<MouseLeaveEvent>(evt => OnMouseHover(evt, input));
|
||||
field.RegisterCallback<DragUpdatedEvent>(OnDragUpdatedEvent);
|
||||
// These callbacks are used for the property dragging scroll behavior
|
||||
field.RegisterCallback<DragEnterEvent>(evt => blackboard.ShowScrollBoundaryRegions());
|
||||
field.RegisterCallback<DragExitedEvent>(evt => blackboard.HideScrollBoundaryRegions());
|
||||
|
||||
// Removing the expand button from the blackboard, its added by default
|
||||
var expandButton = row.Q<Button>("expandButton");
|
||||
expandButton.RemoveFromHierarchy();
|
||||
|
||||
m_InputRows[input] = row;
|
||||
|
||||
if (!addToGraph)
|
||||
{
|
||||
m_InputRows[input].expanded = SessionState.GetBool($"Unity.ShaderGraph.Input.{input.objectId}.isExpanded", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.expanded = true;
|
||||
field.OpenTextEditor();
|
||||
if (input as ShaderKeyword != null)
|
||||
{
|
||||
m_Graph.OnKeywordChangedNoValidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateSelectionAfterUndoRedo(AttachToPanelEvent evt)
|
||||
{
|
||||
var newFieldView = evt.target as BlackboardFieldView;
|
||||
// If this field view represents a value that was previously selected
|
||||
var refName = newFieldView?.shaderInput?.referenceName;
|
||||
if (refName != null && oldSelectionPersistenceData.TryGetValue(refName, out var oldViewDataKey))
|
||||
{
|
||||
// ViewDataKey is how UIElements handles UI state persistence,
|
||||
// This selects the newly added field view
|
||||
newFieldView.viewDataKey = oldViewDataKey;
|
||||
}
|
||||
}
|
||||
|
||||
void DirtyNodes()
|
||||
{
|
||||
foreach (var node in m_Graph.GetNodes<PropertyNode>())
|
||||
{
|
||||
node.OnEnable();
|
||||
node.Dirty(ModificationScope.Node);
|
||||
}
|
||||
foreach (var node in m_Graph.GetNodes<KeywordNode>())
|
||||
{
|
||||
node.OnEnable();
|
||||
node.Dirty(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
|
||||
public BlackboardRow GetBlackboardRow(ShaderInput input)
|
||||
{
|
||||
if (m_InputRows.ContainsKey(input))
|
||||
return m_InputRows[input];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
void OnMouseHover(EventBase evt, ShaderInput input)
|
||||
{
|
||||
var graphView = blackboard.GetFirstAncestorOfType<MaterialGraphView>();
|
||||
if (evt.eventTypeId == MouseEnterEvent.TypeId())
|
||||
{
|
||||
foreach (var node in graphView.nodes.ToList())
|
||||
{
|
||||
if (input is AbstractShaderProperty property)
|
||||
{
|
||||
if (node.userData is PropertyNode propertyNode)
|
||||
{
|
||||
if (propertyNode.property == input)
|
||||
{
|
||||
m_SelectedNodes.Add(node);
|
||||
node.AddToClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (input is ShaderKeyword keyword)
|
||||
{
|
||||
if (node.userData is KeywordNode keywordNode)
|
||||
{
|
||||
if (keywordNode.keyword == input)
|
||||
{
|
||||
m_SelectedNodes.Add(node);
|
||||
node.AddToClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (evt.eventTypeId == MouseLeaveEvent.TypeId() && m_SelectedNodes.Any())
|
||||
{
|
||||
foreach (var node in m_SelectedNodes)
|
||||
{
|
||||
node.RemoveFromClassList("hovered");
|
||||
}
|
||||
m_SelectedNodes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views.Blackboard
|
||||
{
|
||||
class SGBlackboard : GraphSubWindow, ISelection
|
||||
{
|
||||
VisualElement m_ScrollBoundaryTop;
|
||||
VisualElement m_ScrollBoundaryBottom;
|
||||
VisualElement m_BottomResizer;
|
||||
|
||||
bool m_scrollToTop = false;
|
||||
bool m_scrollToBottom = false;
|
||||
bool m_IsFieldBeingDragged = false;
|
||||
|
||||
const int k_DraggedPropertyScrollSpeed = 6;
|
||||
|
||||
public override string windowTitle => "Blackboard";
|
||||
public override string elementName => "SGBlackboard";
|
||||
public override string styleName => "Blackboard";
|
||||
public override string UxmlName => "GraphView/Blackboard";
|
||||
public override string layoutKey => "UnityEditor.ShaderGraph.Blackboard";
|
||||
|
||||
public Action<SGBlackboard> addItemRequested { get; set; }
|
||||
public Action<SGBlackboard, int, VisualElement> moveItemRequested { get; set; }
|
||||
|
||||
public SGBlackboard(GraphView associatedGraphView) : base(associatedGraphView)
|
||||
{
|
||||
windowDockingLayout.dockingLeft = true;
|
||||
|
||||
var addButton = m_MainContainer.Q(name: "addButton") as Button;
|
||||
addButton.clickable.clicked += () => {
|
||||
if (addItemRequested != null)
|
||||
{
|
||||
addItemRequested(this);
|
||||
}
|
||||
};
|
||||
|
||||
associatedGraphView.RegisterCallback<FocusOutEvent>(evt => HideScrollBoundaryRegions());
|
||||
|
||||
// These callbacks make sure the scroll boundary regions don't show up user is not dragging/dropping properties
|
||||
this.RegisterCallback<MouseUpEvent>((evt => HideScrollBoundaryRegions()));
|
||||
this.RegisterCallback<DragExitedEvent>(evt => HideScrollBoundaryRegions());
|
||||
|
||||
m_ScrollBoundaryTop = m_MainContainer.Q(name: "scrollBoundaryTop");
|
||||
m_ScrollBoundaryTop.RegisterCallback<MouseEnterEvent>(ScrollRegionTopEnter);
|
||||
m_ScrollBoundaryTop.RegisterCallback<DragUpdatedEvent>(OnFieldDragUpdate);
|
||||
m_ScrollBoundaryTop.RegisterCallback<MouseLeaveEvent>(ScrollRegionTopLeave);
|
||||
|
||||
m_ScrollBoundaryBottom = m_MainContainer.Q(name: "scrollBoundaryBottom");
|
||||
m_ScrollBoundaryBottom.RegisterCallback<MouseEnterEvent>(ScrollRegionBottomEnter);
|
||||
m_ScrollBoundaryBottom.RegisterCallback<DragUpdatedEvent>(OnFieldDragUpdate);
|
||||
m_ScrollBoundaryBottom.RegisterCallback<MouseLeaveEvent>(ScrollRegionBottomLeave);
|
||||
|
||||
m_BottomResizer = m_MainContainer.Q("bottom-resize");
|
||||
|
||||
HideScrollBoundaryRegions();
|
||||
|
||||
// Sets delegate association so scroll boundary regions are hidden when a blackboard property is dropped into graph
|
||||
if (associatedGraphView is MaterialGraphView materialGraphView)
|
||||
materialGraphView.blackboardFieldDropDelegate = HideScrollBoundaryRegions;
|
||||
|
||||
isWindowScrollable = true;
|
||||
isWindowResizable = true;
|
||||
focusable = true;
|
||||
}
|
||||
|
||||
public void ShowScrollBoundaryRegions()
|
||||
{
|
||||
if (!m_IsFieldBeingDragged && scrollableHeight > 0)
|
||||
{
|
||||
// Interferes with scrolling functionality of properties with the bottom scroll boundary
|
||||
m_BottomResizer.style.visibility = Visibility.Hidden;
|
||||
|
||||
m_IsFieldBeingDragged = true;
|
||||
var contentElement = m_MainContainer.Q(name: "content");
|
||||
scrollViewIndex = contentElement.IndexOf(m_ScrollView);
|
||||
contentElement.Insert(scrollViewIndex, m_ScrollBoundaryTop);
|
||||
scrollViewIndex = contentElement.IndexOf(m_ScrollView);
|
||||
contentElement.Insert(scrollViewIndex + 1, m_ScrollBoundaryBottom);
|
||||
}
|
||||
}
|
||||
|
||||
public void HideScrollBoundaryRegions()
|
||||
{
|
||||
m_BottomResizer.style.visibility = Visibility.Visible;
|
||||
m_IsFieldBeingDragged = false;
|
||||
m_ScrollBoundaryTop.RemoveFromHierarchy();
|
||||
m_ScrollBoundaryBottom.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
int scrollViewIndex { get; set; }
|
||||
|
||||
void ScrollRegionTopEnter(MouseEnterEvent mouseEnterEvent)
|
||||
{
|
||||
if (m_IsFieldBeingDragged)
|
||||
{
|
||||
m_scrollToTop = true;
|
||||
m_scrollToBottom = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollRegionTopLeave(MouseLeaveEvent mouseLeaveEvent)
|
||||
{
|
||||
if (m_IsFieldBeingDragged)
|
||||
m_scrollToTop = false;
|
||||
}
|
||||
|
||||
void ScrollRegionBottomEnter(MouseEnterEvent mouseEnterEvent)
|
||||
{
|
||||
if (m_IsFieldBeingDragged)
|
||||
{
|
||||
m_scrollToBottom = true;
|
||||
m_scrollToTop = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollRegionBottomLeave(MouseLeaveEvent mouseLeaveEvent)
|
||||
{
|
||||
if (m_IsFieldBeingDragged)
|
||||
m_scrollToBottom = false;
|
||||
}
|
||||
|
||||
void OnFieldDragUpdate(DragUpdatedEvent dragUpdatedEvent)
|
||||
{
|
||||
if (m_scrollToTop)
|
||||
m_ScrollView.scrollOffset = new Vector2(m_ScrollView.scrollOffset.x, Mathf.Clamp(m_ScrollView.scrollOffset.y - k_DraggedPropertyScrollSpeed, 0, scrollableHeight));
|
||||
else if (m_scrollToBottom)
|
||||
m_ScrollView.scrollOffset = new Vector2(m_ScrollView.scrollOffset.x, Mathf.Clamp(m_ScrollView.scrollOffset.y + k_DraggedPropertyScrollSpeed, 0, scrollableHeight));
|
||||
}
|
||||
|
||||
public virtual void AddToSelection(ISelectable selectable)
|
||||
{
|
||||
graphView?.AddToSelection(selectable);
|
||||
}
|
||||
|
||||
public virtual void RemoveFromSelection(ISelectable selectable)
|
||||
{
|
||||
graphView?.RemoveFromSelection(selectable);
|
||||
}
|
||||
|
||||
public virtual void ClearSelection()
|
||||
{
|
||||
graphView?.ClearSelection();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,301 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views.Blackboard
|
||||
{
|
||||
public class SGBlackboardSection : GraphElement
|
||||
{
|
||||
VisualElement m_DragIndicator;
|
||||
VisualElement m_MainContainer;
|
||||
VisualElement m_Header;
|
||||
Label m_TitleLabel;
|
||||
VisualElement m_RowsContainer;
|
||||
int m_InsertIndex;
|
||||
|
||||
SGBlackboard m_Blackboard;
|
||||
SGBlackboard blackboard
|
||||
{
|
||||
get { return m_Blackboard ?? (m_Blackboard = GetFirstAncestorOfType<SGBlackboard>()); }
|
||||
}
|
||||
|
||||
public delegate bool CanAcceptDropDelegate(ISelectable selected);
|
||||
|
||||
public CanAcceptDropDelegate canAcceptDrop { get; set; }
|
||||
|
||||
int InsertionIndex(Vector2 pos)
|
||||
{
|
||||
int index = -1;
|
||||
VisualElement owner = contentContainer != null ? contentContainer : this;
|
||||
Vector2 localPos = this.ChangeCoordinatesTo(owner, pos);
|
||||
|
||||
if (owner.ContainsPoint(localPos))
|
||||
{
|
||||
index = 0;
|
||||
|
||||
foreach (VisualElement child in Children())
|
||||
{
|
||||
Rect rect = child.layout;
|
||||
|
||||
if (localPos.y > (rect.y + rect.height / 2))
|
||||
{
|
||||
++index;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
VisualElement FindSectionDirectChild(VisualElement element)
|
||||
{
|
||||
VisualElement directChild = element;
|
||||
|
||||
while ((directChild != null) && (directChild != this))
|
||||
{
|
||||
if (directChild.parent == this)
|
||||
{
|
||||
return directChild;
|
||||
}
|
||||
directChild = directChild.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public SGBlackboardSection()
|
||||
{
|
||||
// Setup VisualElement from Stylesheet and UXML file
|
||||
var tpl = Resources.Load("UXML/GraphView/BlackboardSection") as VisualTreeAsset;
|
||||
m_MainContainer = tpl.Instantiate();
|
||||
m_MainContainer.AddToClassList("mainContainer");
|
||||
|
||||
m_Header = m_MainContainer.Q("sectionHeader");
|
||||
m_TitleLabel = m_MainContainer.Q<Label>("sectionTitleLabel");
|
||||
m_RowsContainer = m_MainContainer.Q("rowsContainer");
|
||||
|
||||
hierarchy.Add(m_MainContainer);
|
||||
|
||||
m_DragIndicator = m_MainContainer.Q("dragIndicator");
|
||||
m_DragIndicator.visible = false;
|
||||
hierarchy.Add(m_DragIndicator);
|
||||
|
||||
ClearClassList();
|
||||
AddToClassList("blackboardSection");
|
||||
|
||||
RegisterCallback<DragUpdatedEvent>(OnDragUpdatedEvent);
|
||||
RegisterCallback<DragPerformEvent>(OnDragPerformEvent);
|
||||
RegisterCallback<DragLeaveEvent>(OnDragLeaveEvent);
|
||||
|
||||
var styleSheet = Resources.Load<StyleSheet>($"Styles/Blackboard");
|
||||
styleSheets.Add(styleSheet);
|
||||
|
||||
m_InsertIndex = -1;
|
||||
}
|
||||
|
||||
public override VisualElement contentContainer { get { return m_RowsContainer; } }
|
||||
|
||||
public override string title
|
||||
{
|
||||
get { return m_TitleLabel.text; }
|
||||
set { m_TitleLabel.text = value; }
|
||||
}
|
||||
|
||||
public bool headerVisible
|
||||
{
|
||||
get { return m_Header.parent != null; }
|
||||
set
|
||||
{
|
||||
if (value == (m_Header.parent != null))
|
||||
return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
m_MainContainer.Add(m_Header);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MainContainer.Remove(m_Header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDragIndicatorVisible(bool visible)
|
||||
{
|
||||
m_DragIndicator.visible = visible;
|
||||
}
|
||||
|
||||
public bool CanAcceptDrop(List<ISelectable> selection)
|
||||
{
|
||||
if (selection == null)
|
||||
return false;
|
||||
|
||||
// Look for at least one selected element in this section to accept drop
|
||||
foreach (ISelectable selected in selection)
|
||||
{
|
||||
VisualElement selectedElement = selected as VisualElement;
|
||||
|
||||
if (selected != null && Contains(selectedElement))
|
||||
{
|
||||
if (canAcceptDrop == null || canAcceptDrop(selected))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnDragUpdatedEvent(DragUpdatedEvent evt)
|
||||
{
|
||||
var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
|
||||
|
||||
if (!CanAcceptDrop(selection))
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
VisualElement sourceItem = null;
|
||||
|
||||
foreach (ISelectable selectedElement in selection)
|
||||
{
|
||||
sourceItem = selectedElement as VisualElement;
|
||||
|
||||
if (sourceItem == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Contains(sourceItem))
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 localPosition = evt.localMousePosition;
|
||||
|
||||
m_InsertIndex = InsertionIndex(localPosition);
|
||||
|
||||
if (m_InsertIndex != -1)
|
||||
{
|
||||
float indicatorY = 0;
|
||||
|
||||
if (m_InsertIndex == childCount)
|
||||
{
|
||||
VisualElement lastChild = this[childCount - 1];
|
||||
|
||||
indicatorY = lastChild.ChangeCoordinatesTo(this, new Vector2(0, lastChild.layout.height + lastChild.resolvedStyle.marginBottom)).y;
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualElement childAtInsertIndex = this[m_InsertIndex];
|
||||
|
||||
indicatorY = childAtInsertIndex.ChangeCoordinatesTo(this, new Vector2(0, -childAtInsertIndex.resolvedStyle.marginTop)).y;
|
||||
}
|
||||
|
||||
SetDragIndicatorVisible(true);
|
||||
|
||||
m_DragIndicator.style.width = layout.width;
|
||||
var newPosition = indicatorY - m_DragIndicator.layout.height / 2;
|
||||
m_DragIndicator.style.top = newPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
|
||||
m_InsertIndex = -1;
|
||||
}
|
||||
|
||||
if (m_InsertIndex != -1)
|
||||
{
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Move;
|
||||
}
|
||||
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
private void OnDragPerformEvent(DragPerformEvent evt)
|
||||
{
|
||||
var selection = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
|
||||
|
||||
if (!CanAcceptDrop(selection))
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_InsertIndex != -1)
|
||||
{
|
||||
List<Tuple<VisualElement, VisualElement>> draggedElements = new List<Tuple<VisualElement, VisualElement>>();
|
||||
|
||||
foreach (ISelectable selectedElement in selection)
|
||||
{
|
||||
var draggedElement = selectedElement as VisualElement;
|
||||
|
||||
if (draggedElement != null && Contains(draggedElement))
|
||||
{
|
||||
draggedElements.Add(new Tuple<VisualElement, VisualElement>(FindSectionDirectChild(draggedElement), draggedElement));
|
||||
}
|
||||
}
|
||||
|
||||
if (draggedElements.Count == 0)
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sorts the dragged elements from their relative order in their parent
|
||||
draggedElements.Sort((pair1, pair2) => { return IndexOf(pair1.Item1).CompareTo(IndexOf(pair2.Item1)); });
|
||||
|
||||
int insertIndex = m_InsertIndex;
|
||||
|
||||
foreach (Tuple<VisualElement, VisualElement> draggedElement in draggedElements)
|
||||
{
|
||||
VisualElement sectionDirectChild = draggedElement.Item1;
|
||||
int indexOfDraggedElement = IndexOf(sectionDirectChild);
|
||||
|
||||
if (!((indexOfDraggedElement == insertIndex) || ((insertIndex - 1) == indexOfDraggedElement)))
|
||||
{
|
||||
if (blackboard.moveItemRequested != null)
|
||||
{
|
||||
blackboard.moveItemRequested(blackboard, m_InsertIndex, draggedElement.Item2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (insertIndex == contentContainer.childCount)
|
||||
{
|
||||
sectionDirectChild.BringToFront();
|
||||
}
|
||||
else
|
||||
{
|
||||
sectionDirectChild.PlaceBehind(this[insertIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (insertIndex > indexOfDraggedElement) // No need to increment the insert index for the next dragged element if the current dragged element is above the current insert location.
|
||||
continue;
|
||||
insertIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
SetDragIndicatorVisible(false);
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
void OnDragLeaveEvent(DragLeaveEvent evt)
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
}
|
||||
|
||||
public void OnDragActionCanceled()
|
||||
{
|
||||
SetDragIndicatorVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
class CategoryColors : ColorProviderFromStyleSheet
|
||||
{
|
||||
public override string GetTitle() => "Category";
|
||||
|
||||
public override bool AllowCustom() => false;
|
||||
public override bool ClearOnDirty() => false;
|
||||
|
||||
protected override bool GetClassFromNode(AbstractMaterialNode node, out string ussClass)
|
||||
{
|
||||
ussClass = string.Empty;
|
||||
if (!(node.GetType().GetCustomAttributes(typeof(TitleAttribute), false).FirstOrDefault() is TitleAttribute title))
|
||||
return false;
|
||||
|
||||
ussClass = title.title[0];
|
||||
|
||||
return !string.IsNullOrEmpty(ussClass);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
// Use this to set colors on your node titles.
|
||||
// There are 2 methods of setting colors - direct Color objects via code (such as data saved in the node itself),
|
||||
// or setting classes on a VisualElement, allowing the colors themselves to be defined in USS. See notes on
|
||||
// IColorProvider for how to use these different methods.
|
||||
class ColorManager
|
||||
{
|
||||
static string DefaultProvider = NoColors.Title;
|
||||
|
||||
List<IColorProvider> m_Providers;
|
||||
|
||||
int m_ActiveIndex = 0;
|
||||
public int activeIndex
|
||||
{
|
||||
get => m_ActiveIndex;
|
||||
private set
|
||||
{
|
||||
if (!IsValidIndex(value))
|
||||
return;
|
||||
|
||||
m_ActiveIndex = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ColorManager(string activeProvider)
|
||||
{
|
||||
m_Providers = new List<IColorProvider>();
|
||||
|
||||
if (string.IsNullOrEmpty(activeProvider))
|
||||
activeProvider = DefaultProvider;
|
||||
|
||||
foreach (var colorType in TypeCache.GetTypesDerivedFrom<IColorProvider>().Where(t => !t.IsAbstract))
|
||||
{
|
||||
var provider = (IColorProvider)Activator.CreateInstance(colorType);
|
||||
m_Providers.Add(provider);
|
||||
}
|
||||
|
||||
m_Providers.Sort((p1, p2) => string.Compare(p1.GetTitle(), p2.GetTitle(), StringComparison.InvariantCulture));
|
||||
activeIndex = m_Providers.FindIndex(provider => provider.GetTitle() == activeProvider);
|
||||
}
|
||||
|
||||
public void SetNodesDirty(IEnumerable<IShaderNodeView> nodeViews)
|
||||
{
|
||||
if (activeProvider.ClearOnDirty())
|
||||
{
|
||||
foreach (var view in nodeViews)
|
||||
{
|
||||
activeProvider.ClearColor(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActiveProvider(int newIndex, IEnumerable<IShaderNodeView> nodeViews)
|
||||
{
|
||||
if (newIndex == activeIndex || !IsValidIndex(newIndex))
|
||||
return;
|
||||
|
||||
var oldProvider = activeProvider;
|
||||
activeIndex = newIndex;
|
||||
|
||||
foreach (var view in nodeViews)
|
||||
{
|
||||
oldProvider.ClearColor(view);
|
||||
activeProvider.ApplyColor(view);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateNodeViews(IEnumerable<IShaderNodeView> nodeViews)
|
||||
{
|
||||
foreach (var view in nodeViews)
|
||||
{
|
||||
UpdateNodeView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateNodeView(IShaderNodeView nodeView)
|
||||
{
|
||||
activeProvider.ApplyColor(nodeView);
|
||||
}
|
||||
|
||||
public IEnumerable<string> providerNames => m_Providers.Select(p => p.GetTitle());
|
||||
|
||||
public string activeProviderName => activeProvider.GetTitle();
|
||||
|
||||
public bool activeSupportsCustom => activeProvider.AllowCustom();
|
||||
|
||||
IColorProvider activeProvider => m_Providers[activeIndex];
|
||||
|
||||
bool IsValidIndex(int index) => index >= 0 && index < m_Providers.Count;
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
[Serializable]
|
||||
class SerializableUserColor
|
||||
{
|
||||
public string Key = String.Empty;
|
||||
public Color Value = Color.black;
|
||||
|
||||
public SerializableUserColor(KeyValuePair<string, Color> pair) { Key = pair.Key; Value = pair.Value; }
|
||||
|
||||
// Empty constructor required by serialization system
|
||||
public SerializableUserColor() {}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CustomColorData : ISerializationCallbackReceiver
|
||||
{
|
||||
Dictionary<string, Color> m_CustomColors = new Dictionary<string, Color>();
|
||||
[SerializeField]
|
||||
List<SerializationHelper.JSONSerializedElement> m_SerializableColors = new List<SerializationHelper.JSONSerializedElement>();
|
||||
|
||||
public bool TryGetColor(string provider, out Color color)
|
||||
{
|
||||
return m_CustomColors.TryGetValue(provider, out color);
|
||||
}
|
||||
|
||||
public void Set(string provider, Color color)
|
||||
{
|
||||
m_CustomColors[provider] = color;
|
||||
}
|
||||
|
||||
public void Remove(string provider)
|
||||
{
|
||||
m_CustomColors.Remove(provider);
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
m_SerializableColors.Clear();
|
||||
foreach (var customColorKvp in m_CustomColors)
|
||||
{
|
||||
m_SerializableColors.Add(SerializationHelper.Serialize(new SerializableUserColor(customColorKvp)));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
List<SerializableUserColor> colors = SerializationHelper.Deserialize<SerializableUserColor>(m_SerializableColors, null);
|
||||
foreach (var colorPair in colors)
|
||||
{
|
||||
m_CustomColors.Add(colorPair.Key, colorPair.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
// Defines how the ColorManager interacts with various providers
|
||||
interface IColorProvider
|
||||
{
|
||||
string GetTitle();
|
||||
|
||||
bool AllowCustom();
|
||||
|
||||
bool ClearOnDirty();
|
||||
|
||||
void ApplyColor(IShaderNodeView nodeView);
|
||||
void ClearColor(IShaderNodeView nodeView);
|
||||
}
|
||||
|
||||
internal abstract class ColorProviderFromCode : IColorProvider
|
||||
{
|
||||
protected abstract bool GetColorFromNode(AbstractMaterialNode node, out Color color);
|
||||
|
||||
public abstract string GetTitle();
|
||||
|
||||
public abstract bool AllowCustom();
|
||||
|
||||
public abstract bool ClearOnDirty();
|
||||
|
||||
public virtual void ApplyColor(IShaderNodeView nodeView)
|
||||
{
|
||||
if (GetColorFromNode(nodeView.node, out var color))
|
||||
{
|
||||
nodeView.SetColor(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeView.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
nodeView.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class ColorProviderFromStyleSheet : IColorProvider
|
||||
{
|
||||
protected abstract bool GetClassFromNode(AbstractMaterialNode node, out string ussClass);
|
||||
|
||||
public abstract string GetTitle();
|
||||
|
||||
public abstract bool AllowCustom();
|
||||
|
||||
public abstract bool ClearOnDirty();
|
||||
|
||||
public virtual void ApplyColor(IShaderNodeView nodeView)
|
||||
{
|
||||
if (GetClassFromNode(nodeView.node, out var ussClass))
|
||||
{
|
||||
nodeView.colorElement.AddToClassList(ussClass);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
if (GetClassFromNode(nodeView.node, out var ussClass))
|
||||
{
|
||||
nodeView.colorElement.RemoveFromClassList(ussClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
internal class NoColors : IColorProvider
|
||||
{
|
||||
public const string Title = "<None>";
|
||||
public string GetTitle() => Title;
|
||||
|
||||
public bool AllowCustom() => false;
|
||||
public bool ClearOnDirty() => false;
|
||||
|
||||
public void ApplyColor(IShaderNodeView nodeView)
|
||||
{
|
||||
}
|
||||
|
||||
public void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
class PrecisionColors : ColorProviderFromStyleSheet
|
||||
{
|
||||
public override string GetTitle() => "Precision";
|
||||
|
||||
public override bool AllowCustom() => false;
|
||||
|
||||
public override bool ClearOnDirty() => true;
|
||||
|
||||
protected override bool GetClassFromNode(AbstractMaterialNode node, out string ussClass)
|
||||
{
|
||||
ussClass = node.concretePrecision.ToString();
|
||||
|
||||
return !string.IsNullOrEmpty(ussClass);
|
||||
}
|
||||
|
||||
public override void ClearColor(IShaderNodeView nodeView)
|
||||
{
|
||||
foreach (var type in ConcretePrecision.GetValues(typeof(ConcretePrecision)))
|
||||
{
|
||||
nodeView.colorElement.RemoveFromClassList(type.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Colors
|
||||
{
|
||||
class UserColors : ColorProviderFromCode
|
||||
{
|
||||
const string m_Title = "User Defined";
|
||||
public override string GetTitle() => m_Title;
|
||||
|
||||
public override bool AllowCustom() => true;
|
||||
public override bool ClearOnDirty() => false;
|
||||
|
||||
protected override bool GetColorFromNode(AbstractMaterialNode node, out Color color)
|
||||
{
|
||||
color = Color.black;
|
||||
return node.TryGetColor(m_Title, ref color);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views.Blackboard
|
||||
{
|
||||
class BlackboardController : SGViewController<BlackboardViewModel>
|
||||
{
|
||||
IList<SGBlackboardSection> m_BlackboardSections;
|
||||
|
||||
public BlackboardController(BlackboardViewModel viewModel, GraphData graphData)
|
||||
: base(viewModel, graphData)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ModelChanged(GraphData graphData)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,165 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
struct SGControllerChangedEvent
|
||||
{
|
||||
public ISGControlledElement target;
|
||||
public SGController controller;
|
||||
public int change;
|
||||
|
||||
bool m_PropagationStopped;
|
||||
public void StopPropagation()
|
||||
{
|
||||
m_PropagationStopped = true;
|
||||
}
|
||||
|
||||
public bool isPropagationStopped
|
||||
{ get { return m_PropagationStopped; } }
|
||||
}
|
||||
|
||||
class SGControllerEvent
|
||||
{
|
||||
public ISGControlledElement target = null;
|
||||
|
||||
SGControllerEvent(ISGControlledElement controlledTarget)
|
||||
{
|
||||
target = controlledTarget;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SGController
|
||||
{
|
||||
}
|
||||
|
||||
abstract class SGController<T> : SGController
|
||||
{
|
||||
public bool m_DisableCalled = false;
|
||||
public virtual void OnDisable()
|
||||
{
|
||||
if (m_DisableCalled)
|
||||
Debug.LogError(GetType().Name + ".Disable called twice");
|
||||
|
||||
m_DisableCalled = true;
|
||||
foreach (var element in allChildren)
|
||||
{
|
||||
Profiler.BeginSample(element.GetType().Name + ".OnDisable");
|
||||
element.OnDisable();
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterHandler(ISGControlledElement handler)
|
||||
{
|
||||
//Debug.Log("RegisterHandler of " + handler.GetType().Name + " on " + GetType().Name );
|
||||
|
||||
if (m_EventHandlers.Contains(handler))
|
||||
Debug.LogError("Handler registered twice");
|
||||
else
|
||||
{
|
||||
m_EventHandlers.Add(handler);
|
||||
|
||||
NotifyEventHandler(handler, AnyThing);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterHandler(ISGControlledElement handler)
|
||||
{
|
||||
m_EventHandlers.Remove(handler);
|
||||
}
|
||||
|
||||
public const int AnyThing = -1;
|
||||
|
||||
protected void NotifyChange(int eventID)
|
||||
{
|
||||
var eventHandlers = m_EventHandlers.ToArray(); // Some notification may trigger Register/Unregister so duplicate the collection.
|
||||
|
||||
foreach (var eventHandler in eventHandlers)
|
||||
{
|
||||
Profiler.BeginSample("NotifyChange:" + eventHandler.GetType().Name);
|
||||
NotifyEventHandler(eventHandler, eventID);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyEventHandler(ISGControlledElement eventHandler, int eventID)
|
||||
{
|
||||
SGControllerChangedEvent e = new SGControllerChangedEvent();
|
||||
e.controller = this;
|
||||
e.target = eventHandler;
|
||||
e.change = eventID;
|
||||
eventHandler.OnControllerChanged(ref e);
|
||||
if (e.isPropagationStopped)
|
||||
return;
|
||||
if (eventHandler is VisualElement)
|
||||
{
|
||||
var element = eventHandler as VisualElement;
|
||||
eventHandler = element.GetFirstOfType<ISGControlledElement>();
|
||||
while (eventHandler != null)
|
||||
{
|
||||
eventHandler.OnControllerChanged(ref e);
|
||||
if (e.isPropagationStopped)
|
||||
break;
|
||||
eventHandler = (eventHandler as VisualElement).GetFirstAncestorOfType<ISGControlledElement>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendEvent(SGControllerEvent e)
|
||||
{
|
||||
var eventHandlers = m_EventHandlers.ToArray(); // Some notification may trigger Register/Unregister so duplicate the collection.
|
||||
|
||||
foreach (var eventHandler in eventHandlers)
|
||||
{
|
||||
eventHandler.OnControllerEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void ApplyChanges();
|
||||
|
||||
|
||||
public virtual IEnumerable<SGController<T>> allChildren
|
||||
{
|
||||
get { return Enumerable.Empty<SGController<T>>(); }
|
||||
}
|
||||
|
||||
List<ISGControlledElement> m_EventHandlers = new List<ISGControlledElement>();
|
||||
}
|
||||
|
||||
// Using the Curiously Recurring Template Pattern here
|
||||
// Generic subclass that provides itself as argument for base class type
|
||||
// Allows access to child class functionality in the parent
|
||||
abstract class SGViewController<T> : SGController<SGViewController<T>> where T : SGViewModel
|
||||
{
|
||||
// Holds application specific data
|
||||
GraphData m_Model;
|
||||
|
||||
// Holds data specific to the views this controller is responsible for
|
||||
T m_ViewModel;
|
||||
protected SGViewController(T viewModel, GraphData graphData)
|
||||
{
|
||||
m_ViewModel = viewModel;
|
||||
m_Model = graphData;
|
||||
m_ViewModel.ConstructFromModel(m_Model);
|
||||
}
|
||||
|
||||
protected abstract void ModelChanged(GraphData graphData);
|
||||
|
||||
public override void ApplyChanges()
|
||||
{
|
||||
ModelChanged(Model);
|
||||
ViewModel.ConstructFromModel(Model);
|
||||
foreach (var controller in allChildren)
|
||||
{
|
||||
controller.ApplyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public T ViewModel => m_ViewModel;
|
||||
public GraphData Model => m_Model;
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ButtonControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public ButtonControlAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ButtonControlView(node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
struct ButtonConfig
|
||||
{
|
||||
public string text;
|
||||
public Action action;
|
||||
}
|
||||
|
||||
class ButtonControlView : VisualElement
|
||||
{
|
||||
public ButtonControlView(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
|
||||
m_Node = node;
|
||||
|
||||
Type type = propertyInfo.PropertyType;
|
||||
if (type != typeof(ButtonConfig))
|
||||
{
|
||||
throw new ArgumentException("Property must be a ButtonConfig.", "propertyInfo");
|
||||
}
|
||||
var value = (ButtonConfig)propertyInfo.GetValue(m_Node, null);
|
||||
|
||||
Add(new Button(value.action) { text = value.text});
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ChannelEnumControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
int m_SlotId;
|
||||
|
||||
public ChannelEnumControlAttribute(string label = null, int slotId = 0)
|
||||
{
|
||||
m_Label = label;
|
||||
m_SlotId = slotId;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ChannelEnumControlView(m_Label, m_SlotId, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelEnumControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
int m_SlotId;
|
||||
|
||||
PopupField<string> m_PopupField;
|
||||
string[] m_ValueNames;
|
||||
|
||||
int m_PreviousChannelCount = -1;
|
||||
|
||||
public ChannelEnumControlView(string label, int slotId, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ChannelEnumControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
m_SlotId = slotId;
|
||||
if (!propertyInfo.PropertyType.IsEnum)
|
||||
throw new ArgumentException("Property must be an enum.", "propertyInfo");
|
||||
Add(new Label(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)));
|
||||
|
||||
var value = (Enum)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_ValueNames = Enum.GetNames(value.GetType());
|
||||
|
||||
CreatePopup();
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<string> evt)
|
||||
{
|
||||
var index = m_PopupField.index;
|
||||
var value = (int)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (!index.Equals(value))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, index, null);
|
||||
}
|
||||
|
||||
CreatePopup();
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
if (scope == ModificationScope.Node)
|
||||
{
|
||||
CreatePopup();
|
||||
m_PopupField.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
void CreatePopup()
|
||||
{
|
||||
int channelCount = SlotValueHelper.GetChannelCount(m_Node.FindSlot<MaterialSlot>(m_SlotId).concreteValueType);
|
||||
|
||||
if (m_PopupField != null)
|
||||
{
|
||||
if (channelCount == m_PreviousChannelCount)
|
||||
return;
|
||||
|
||||
Remove(m_PopupField);
|
||||
}
|
||||
|
||||
m_PreviousChannelCount = channelCount;
|
||||
List<string> popupEntries = new List<string>();
|
||||
for (int i = 0; i < channelCount; i++)
|
||||
popupEntries.Add(m_ValueNames[i]);
|
||||
|
||||
var value = (int)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (value >= channelCount)
|
||||
value = 0;
|
||||
|
||||
m_PopupField = new PopupField<string>(popupEntries, value);
|
||||
m_PopupField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(m_PopupField);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ChannelEnumMaskControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
int m_SlotId;
|
||||
|
||||
public ChannelEnumMaskControlAttribute(string label = null, int slotId = 0)
|
||||
{
|
||||
m_Label = label;
|
||||
m_SlotId = slotId;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ChannelEnumMaskControlView(m_Label, m_SlotId, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelEnumMaskControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
string m_Label;
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
IMGUIContainer m_Container;
|
||||
int m_SlotId;
|
||||
|
||||
public ChannelEnumMaskControlView(string label, int slotId, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ChannelEnumMaskControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
m_SlotId = slotId;
|
||||
//if (!propertyInfo.PropertyType.IsEnum)
|
||||
//throw new ArgumentException("Property must be an enum.", "propertyInfo");
|
||||
m_Label = label;
|
||||
m_Container = new IMGUIContainer(OnGUIHandler);
|
||||
Add(m_Container);
|
||||
}
|
||||
|
||||
void OnGUIHandler()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Label(m_Label);
|
||||
UpdatePopup();
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
if (scope == ModificationScope.Graph)
|
||||
m_Container.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
private void UpdatePopup()
|
||||
{
|
||||
var value = (int)m_PropertyInfo.GetValue(m_Node, null);
|
||||
using (var changeCheckScope = new EditorGUI.ChangeCheckScope())
|
||||
{
|
||||
int channelCount = SlotValueHelper.GetChannelCount(m_Node.FindSlot<MaterialSlot>(m_SlotId).concreteValueType);
|
||||
string[] enumEntryNames = Enum.GetNames(typeof(TextureChannel));
|
||||
string[] popupEntries = new string[channelCount];
|
||||
for (int i = 0; i < popupEntries.Length; i++)
|
||||
popupEntries[i] = enumEntryNames[i];
|
||||
value = EditorGUILayout.MaskField("", value, popupEntries, GUILayout.Width(80f));
|
||||
|
||||
if (changeCheckScope.changed)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ChannelMixerControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
float m_Minimum;
|
||||
float m_Maximum;
|
||||
|
||||
public ChannelMixerControlAttribute(string label = null, float minimum = -2f, float maximum = 2f)
|
||||
{
|
||||
m_Label = label;
|
||||
m_Minimum = minimum;
|
||||
m_Maximum = maximum;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ChannelMixerControlView(m_Label, m_Minimum, m_Maximum, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelMixerControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
ChannelMixerNode.ChannelMixer m_ChannelMixer;
|
||||
int m_OutChannel;
|
||||
|
||||
Slider m_RedSlider;
|
||||
Slider m_GreenSlider;
|
||||
Slider m_BlueSlider;
|
||||
|
||||
FloatField m_RedInputField;
|
||||
FloatField m_GreenInputField;
|
||||
FloatField m_BlueInputField;
|
||||
|
||||
float m_Minimum;
|
||||
float m_Maximum;
|
||||
bool m_Initialized;
|
||||
|
||||
public ChannelMixerControlView(string label, float minimum, float maximum, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ChannelMixerControlView"));
|
||||
m_ChannelMixer = (ChannelMixerNode.ChannelMixer)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_OutChannel = 0;
|
||||
|
||||
m_Minimum = minimum;
|
||||
m_Maximum = maximum;
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(ChannelMixerNode.ChannelMixer))
|
||||
throw new ArgumentException("Property must be of type ChannelMixer.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var buttonPanel = new VisualElement { name = "buttonPanel" };
|
||||
|
||||
Action changedOutputRed = () => OnClickButton(0);
|
||||
var outputButtonRed = new Button(changedOutputRed);
|
||||
outputButtonRed.Add(new Label("R"));
|
||||
buttonPanel.Add(outputButtonRed);
|
||||
|
||||
Action changedOutputGreen = () => OnClickButton(1);
|
||||
var outputButtonGreen = new Button(changedOutputGreen);
|
||||
outputButtonGreen.Add(new Label("G"));
|
||||
buttonPanel.Add(outputButtonGreen);
|
||||
|
||||
Action changedOutputBlue = () => OnClickButton(2);
|
||||
var outputButtonBlue = new Button(changedOutputBlue);
|
||||
outputButtonBlue.Add(new Label("B"));
|
||||
buttonPanel.Add(outputButtonBlue);
|
||||
|
||||
Add(buttonPanel);
|
||||
|
||||
var redSliderPanel = new VisualElement { name = "sliderPanel" };
|
||||
redSliderPanel.Add(new Label("R"));
|
||||
Action<float> changedRedIn = (s) => { OnChangeSlider(s, 0); };
|
||||
m_RedSlider = new Slider(m_Minimum, m_Maximum);
|
||||
m_RedSlider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue, 0));
|
||||
|
||||
redSliderPanel.Add(m_RedSlider);
|
||||
m_RedInputField = new FloatField { value = m_ChannelMixer.outRed.x };
|
||||
m_RedInputField.RegisterCallback<ChangeEvent<double>, int>(OnChangeInputField, 0);
|
||||
redSliderPanel.Add(m_RedInputField);
|
||||
Add(redSliderPanel);
|
||||
|
||||
var greenSliderPanel = new VisualElement { name = "sliderPanel" };
|
||||
greenSliderPanel.Add(new Label("G"));
|
||||
m_GreenSlider = new Slider(m_Minimum, m_Maximum);
|
||||
m_GreenSlider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue, 1));
|
||||
greenSliderPanel.Add(m_GreenSlider);
|
||||
m_GreenInputField = new FloatField { value = m_ChannelMixer.outRed.y };
|
||||
m_GreenInputField.RegisterCallback<ChangeEvent<double>, int>(OnChangeInputField, 1);
|
||||
greenSliderPanel.Add(m_GreenInputField);
|
||||
Add(greenSliderPanel);
|
||||
|
||||
var blueSliderPanel = new VisualElement { name = "sliderPanel" };
|
||||
blueSliderPanel.Add(new Label("B"));
|
||||
m_BlueSlider = new Slider(m_Minimum, m_Maximum);
|
||||
m_BlueSlider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue, 2));
|
||||
blueSliderPanel.Add(m_BlueSlider);
|
||||
m_BlueInputField = new FloatField { value = m_ChannelMixer.outRed.z };
|
||||
m_BlueInputField.RegisterCallback<ChangeEvent<double>, int>(OnChangeInputField, 2);
|
||||
blueSliderPanel.Add(m_BlueInputField);
|
||||
Add(blueSliderPanel);
|
||||
|
||||
m_Initialized = true;
|
||||
ResetSliders();
|
||||
}
|
||||
|
||||
void ResetSliders()
|
||||
{
|
||||
Vector3 outputChannel = GetOutputChannel();
|
||||
m_RedSlider.value = outputChannel[0];
|
||||
m_GreenSlider.value = outputChannel[1];
|
||||
m_BlueSlider.value = outputChannel[2];
|
||||
}
|
||||
|
||||
void OnChangeSlider(float value, int inChannel)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
return;
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
switch (m_OutChannel)
|
||||
{
|
||||
case 1:
|
||||
m_ChannelMixer.outGreen[inChannel] = value;
|
||||
break;
|
||||
case 2:
|
||||
m_ChannelMixer.outBlue[inChannel] = value;
|
||||
break;
|
||||
default:
|
||||
m_ChannelMixer.outRed[inChannel] = value;
|
||||
break;
|
||||
}
|
||||
switch (inChannel)
|
||||
{
|
||||
case 1:
|
||||
m_GreenInputField.value = value;
|
||||
break;
|
||||
case 2:
|
||||
m_BlueInputField.value = value;
|
||||
break;
|
||||
default:
|
||||
m_RedInputField.value = value;
|
||||
break;
|
||||
}
|
||||
m_PropertyInfo.SetValue(m_Node, m_ChannelMixer, null);
|
||||
}
|
||||
|
||||
void OnChangeInputField(ChangeEvent<double> evt, int inChannel)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
return;
|
||||
var value = Mathf.Max(Mathf.Min((float)evt.newValue, m_Maximum), m_Minimum);
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Input Field Change");
|
||||
switch (m_OutChannel)
|
||||
{
|
||||
case 1:
|
||||
m_ChannelMixer.outGreen[inChannel] = value;
|
||||
break;
|
||||
case 2:
|
||||
m_ChannelMixer.outBlue[inChannel] = value;
|
||||
break;
|
||||
default:
|
||||
m_ChannelMixer.outRed[inChannel] = value;
|
||||
break;
|
||||
}
|
||||
switch (inChannel)
|
||||
{
|
||||
case 1:
|
||||
m_GreenSlider.value = value;
|
||||
break;
|
||||
case 2:
|
||||
m_BlueSlider.value = value;
|
||||
break;
|
||||
default:
|
||||
m_RedSlider.value = value;
|
||||
break;
|
||||
}
|
||||
m_PropertyInfo.SetValue(m_Node, m_ChannelMixer, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void OnClickButton(int outChannel)
|
||||
{
|
||||
m_OutChannel = outChannel;
|
||||
ResetSliders();
|
||||
}
|
||||
|
||||
Vector3 GetOutputChannel()
|
||||
{
|
||||
switch (m_OutChannel)
|
||||
{
|
||||
case 1:
|
||||
return m_ChannelMixer.outGreen;
|
||||
case 2:
|
||||
return m_ChannelMixer.outBlue;
|
||||
default:
|
||||
return m_ChannelMixer.outRed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using Color = UnityEditor.ShaderGraph.ColorNode.Color;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ColorControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
ColorMode m_ColorMode;
|
||||
|
||||
public ColorControlAttribute(string label = null, ColorMode colorMode = ColorMode.Default)
|
||||
{
|
||||
m_Label = label;
|
||||
m_ColorMode = colorMode;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ColorControlView(m_Label, m_ColorMode, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ColorControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
Color m_Color;
|
||||
ColorField m_ColorField;
|
||||
|
||||
public ColorControlView(string label, ColorMode colorMode, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ColorControlView"));
|
||||
if (propertyInfo.PropertyType != typeof(Color))
|
||||
throw new ArgumentException("Property must be of type Color.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
m_Color = (Color)m_PropertyInfo.GetValue(m_Node, null);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
m_ColorField = new ColorField { value = m_Color.color, hdr = m_Color.mode == ColorMode.HDR, showEyeDropper = false };
|
||||
m_ColorField.RegisterValueChangedCallback(OnChange);
|
||||
Add(m_ColorField);
|
||||
|
||||
VisualElement enumPanel = new VisualElement { name = "enumPanel" };
|
||||
enumPanel.Add(new Label("Mode"));
|
||||
var enumField = new EnumField(m_Color.mode);
|
||||
enumField.RegisterValueChangedCallback(OnModeChanged);
|
||||
enumPanel.Add(enumField);
|
||||
Add(enumPanel);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Color> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Color Change");
|
||||
m_Color.color = evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Color, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void OnModeChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
if (!evt.newValue.Equals(m_Color.mode))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_Color.mode = (ColorMode)evt.newValue;
|
||||
m_ColorField.hdr = m_Color.mode == ColorMode.HDR;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Color, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class CubemapControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public CubemapControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new CubemapControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class CubemapControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public CubemapControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Cubemap))
|
||||
throw new ArgumentException("Property must be of type Texture.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var cubemapField = new ObjectField { value = (Cubemap)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Cubemap) };
|
||||
cubemapField.RegisterValueChangedCallback(OnChange);
|
||||
Add(cubemapField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Cubemap Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class DefaultControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (propertyInfo.PropertyType == typeof(Color))
|
||||
return new ColorControlView(null, ColorMode.Default, node, propertyInfo);
|
||||
if (typeof(Enum).IsAssignableFrom(propertyInfo.PropertyType))
|
||||
return new EnumControlView(null, node, propertyInfo);
|
||||
if (propertyInfo.PropertyType == typeof(Texture2D))
|
||||
return new TextureControlView(null, node, propertyInfo);
|
||||
if (propertyInfo.PropertyType == typeof(Texture2DArray))
|
||||
return new TextureArrayControlView(null, node, propertyInfo);
|
||||
if (propertyInfo.PropertyType == typeof(Texture3D))
|
||||
return new Texture3DControlView(null, node, propertyInfo);
|
||||
if (MultiFloatControlView.validTypes.Contains(propertyInfo.PropertyType))
|
||||
return new MultiFloatControlView(null, "X", "Y", "Z", "W", node, propertyInfo);
|
||||
if (typeof(Object).IsAssignableFrom(propertyInfo.PropertyType))
|
||||
return new ObjectControlView(null, node, propertyInfo);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class DielectricSpecularControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public DielectricSpecularControlAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new DielectricSpecularControlView(node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class DielectricSpecularControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
DielectricSpecularNode.DielectricMaterial m_DielectricMaterial;
|
||||
|
||||
VisualElement m_RangePanel;
|
||||
Slider m_RangeSlider;
|
||||
FloatField m_RangeField;
|
||||
VisualElement m_IORPanel;
|
||||
Slider m_IORSlider;
|
||||
FloatField m_IORField;
|
||||
|
||||
public DielectricSpecularControlView(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/DielectricSpecularControlView"));
|
||||
m_DielectricMaterial = (DielectricSpecularNode.DielectricMaterial)m_PropertyInfo.GetValue(m_Node, null);
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(DielectricSpecularNode.DielectricMaterial))
|
||||
throw new ArgumentException("Property must be of type DielectricMaterial.", "propertyInfo");
|
||||
|
||||
var enumPanel = new VisualElement { name = "enumPanel" };
|
||||
enumPanel.Add(new Label("Material"));
|
||||
var enumField = new EnumField(m_DielectricMaterial.type);
|
||||
enumField.RegisterValueChangedCallback(OnEnumChanged);
|
||||
enumPanel.Add(enumField);
|
||||
Add(enumPanel);
|
||||
|
||||
m_RangePanel = new VisualElement { name = "sliderPanel" };
|
||||
m_RangePanel.Add(new Label("Range"));
|
||||
m_RangeSlider = new Slider(0.01f, 1) { value = m_DielectricMaterial.range };
|
||||
m_RangeSlider.RegisterValueChangedCallback((evt) => OnChangeRangeSlider(evt.newValue));
|
||||
|
||||
m_RangePanel.Add(m_RangeSlider);
|
||||
m_RangeField = AddField(m_RangePanel, m_RangeSlider, 0, m_DielectricMaterial);
|
||||
m_RangePanel.SetEnabled(m_DielectricMaterial.type == DielectricMaterialType.Common);
|
||||
Add(m_RangePanel);
|
||||
|
||||
m_IORPanel = new VisualElement { name = "sliderPanel" };
|
||||
m_IORPanel.Add(new Label("IOR"));
|
||||
m_IORSlider = new Slider(1, 2.5f) { value = m_DielectricMaterial.indexOfRefraction };
|
||||
m_IORSlider.RegisterValueChangedCallback((evt) => OnChangeIORSlider(evt.newValue));
|
||||
|
||||
m_IORPanel.Add(m_IORSlider);
|
||||
m_IORField = AddField(m_IORPanel, m_IORSlider, 1, m_DielectricMaterial);
|
||||
m_IORPanel.SetEnabled(m_DielectricMaterial.type == DielectricMaterialType.Custom);
|
||||
Add(m_IORPanel);
|
||||
}
|
||||
|
||||
void OnEnumChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
if (!evt.newValue.Equals(m_DielectricMaterial.type))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_DielectricMaterial.type = (DielectricMaterialType)evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
|
||||
switch (m_DielectricMaterial.type)
|
||||
{
|
||||
case DielectricMaterialType.Common:
|
||||
m_RangePanel.SetEnabled(true);
|
||||
m_IORPanel.SetEnabled(false);
|
||||
break;
|
||||
case DielectricMaterialType.Custom:
|
||||
m_RangePanel.SetEnabled(false);
|
||||
m_IORPanel.SetEnabled(true);
|
||||
break;
|
||||
default:
|
||||
m_RangePanel.SetEnabled(false);
|
||||
m_IORPanel.SetEnabled(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeRangeSlider(float newValue)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
m_DielectricMaterial.range = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
if (m_RangeField != null)
|
||||
m_RangeField.value = newValue;
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void OnChangeIORSlider(float newValue)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
m_DielectricMaterial.indexOfRefraction = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
if (m_IORField != null)
|
||||
m_IORField.value = newValue;
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
FloatField AddField(VisualElement panel, Slider slider, int index, DielectricSpecularNode.DielectricMaterial initMaterial)
|
||||
{
|
||||
float initValue;
|
||||
if (index == 1)
|
||||
initValue = initMaterial.indexOfRefraction;
|
||||
else
|
||||
initValue = initMaterial.range;
|
||||
|
||||
var field = new FloatField { userData = index, value = initValue };
|
||||
|
||||
field.RegisterCallback<MouseDownEvent>(Repaint);
|
||||
field.RegisterCallback<MouseMoveEvent>(Repaint);
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
var fieldValue = (float)evt.newValue;
|
||||
if (index == 1)
|
||||
m_DielectricMaterial.indexOfRefraction = fieldValue;
|
||||
else
|
||||
m_DielectricMaterial.range = fieldValue;
|
||||
|
||||
m_PropertyInfo.SetValue(m_Node, m_DielectricMaterial, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
field.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
if (index == 1)
|
||||
RedrawIORControls(m_DielectricMaterial.indexOfRefraction);
|
||||
else
|
||||
RedrawRangeControls(m_DielectricMaterial.range);
|
||||
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
panel.Add(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
void RedrawRangeControls(float value)
|
||||
{
|
||||
value = Mathf.Max(Mathf.Min(value, 1), 0.01f);
|
||||
m_RangePanel.Remove(m_RangeSlider);
|
||||
m_RangeSlider = new Slider(0.01f, 1) { value = value };
|
||||
m_RangeSlider.RegisterValueChangedCallback((evt) => OnChangeRangeSlider(evt.newValue));
|
||||
m_RangePanel.Add(m_RangeSlider);
|
||||
m_RangePanel.Remove(m_RangeField);
|
||||
m_RangeField.value = value;
|
||||
m_RangePanel.Add(m_RangeField);
|
||||
}
|
||||
|
||||
void RedrawIORControls(float value)
|
||||
{
|
||||
value = Mathf.Max(Mathf.Min(value, 5), 1);
|
||||
m_IORPanel.Remove(m_IORSlider);
|
||||
m_IORSlider = new Slider(1, 2.5f) { value = value };
|
||||
m_IORSlider.RegisterValueChangedCallback((evt) => OnChangeIORSlider(evt.newValue));
|
||||
|
||||
m_IORPanel.Add(m_IORSlider);
|
||||
m_IORPanel.Remove(m_IORField);
|
||||
m_IORField.value = value;
|
||||
m_IORPanel.Add(m_IORField);
|
||||
}
|
||||
|
||||
void Repaint<T>(MouseEventBase<T> evt) where T : MouseEventBase<T>, new()
|
||||
{
|
||||
evt.StopPropagation();
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class EnumControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public EnumControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new EnumControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class EnumControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public EnumControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/EnumControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (!propertyInfo.PropertyType.IsEnum)
|
||||
throw new ArgumentException("Property must be an enum.", "propertyInfo");
|
||||
Add(new Label(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)));
|
||||
var enumField = new EnumField((Enum)m_PropertyInfo.GetValue(m_Node, null));
|
||||
enumField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(enumField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var value = (Enum)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (!evt.newValue.Equals(value))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
interface IEnumConversion
|
||||
{
|
||||
Enum from { get; set; }
|
||||
Enum to { get; set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class EnumConversionControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new EnumConversionControlView(node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class EnumConversionControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
IEnumConversion value
|
||||
{
|
||||
get { return (IEnumConversion)m_PropertyInfo.GetValue(m_Node, null); }
|
||||
set { m_PropertyInfo.SetValue(m_Node, value, null); }
|
||||
}
|
||||
|
||||
public EnumConversionControlView(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!propertyInfo.PropertyType.GetInterfaces().Any(t => t == typeof(IEnumConversion)))
|
||||
throw new ArgumentException("Property type must implement IEnumConversion.");
|
||||
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/EnumConversionControlView"));
|
||||
var currentValue = value;
|
||||
|
||||
var ec = (IEnumConversion)propertyInfo.GetValue(m_Node, null);
|
||||
propertyInfo.SetValue(m_Node, ec, null);
|
||||
|
||||
var fromField = new EnumField(currentValue.from);
|
||||
fromField.RegisterValueChangedCallback(OnFromChanged);
|
||||
Add(fromField);
|
||||
|
||||
var arrowLabel = new Label("➔");
|
||||
Add(arrowLabel);
|
||||
|
||||
var toField = new EnumField(currentValue.to);
|
||||
toField.RegisterValueChangedCallback(OnToChanged);
|
||||
Add(toField);
|
||||
}
|
||||
|
||||
void OnFromChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var currentValue = value;
|
||||
if (!Equals(currentValue.from, evt.newValue))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change Colorspace From");
|
||||
currentValue.from = evt.newValue;
|
||||
value = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
void OnToChanged(ChangeEvent<Enum> evt)
|
||||
{
|
||||
var currentValue = value;
|
||||
if (!Equals(currentValue.to, evt.newValue))
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change Colorspace To");
|
||||
currentValue.to = evt.newValue;
|
||||
value = currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class GradientControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public GradientControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new GradientControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
class GradientObject : ScriptableObject
|
||||
{
|
||||
public Gradient gradient = new Gradient();
|
||||
}
|
||||
|
||||
class GradientControlView : VisualElement
|
||||
{
|
||||
GUIContent m_Label;
|
||||
|
||||
AbstractMaterialNode m_Node;
|
||||
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
[SerializeField]
|
||||
GradientObject m_GradientObject;
|
||||
|
||||
[SerializeField]
|
||||
SerializedObject m_SerializedObject;
|
||||
|
||||
public GradientControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/GradientControlView"));
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(Gradient))
|
||||
throw new ArgumentException("Property must be of type Gradient.", "propertyInfo");
|
||||
new GUIContent(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name));
|
||||
|
||||
m_GradientObject = ScriptableObject.CreateInstance<GradientObject>();
|
||||
m_GradientObject.gradient = new Gradient();
|
||||
m_SerializedObject = new SerializedObject(m_GradientObject);
|
||||
|
||||
var gradient = (Gradient)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_GradientObject.gradient.SetKeys(gradient.colorKeys, gradient.alphaKeys);
|
||||
m_GradientObject.gradient.mode = gradient.mode;
|
||||
|
||||
var gradientPanel = new VisualElement { name = "gradientPanel" };
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
gradientPanel.Add(new Label(label));
|
||||
|
||||
var gradientField = new GradientField() { value = m_GradientObject.gradient , colorSpace = ColorSpace.Linear};
|
||||
gradientField.RegisterValueChangedCallback(OnValueChanged);
|
||||
gradientPanel.Add(gradientField);
|
||||
|
||||
Add(gradientPanel);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Gradient> evt)
|
||||
{
|
||||
m_SerializedObject.Update();
|
||||
var value = (Gradient)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (!evt.newValue.Equals(value))
|
||||
{
|
||||
m_GradientObject.gradient.SetKeys(evt.newValue.colorKeys, evt.newValue.alphaKeys);
|
||||
m_GradientObject.gradient.mode = evt.newValue.mode;
|
||||
m_SerializedObject.ApplyModifiedProperties();
|
||||
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, m_GradientObject.gradient, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
using System.Reflection;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
interface IControlAttribute
|
||||
{
|
||||
VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo);
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class IdentifierControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public IdentifierControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new IdentifierControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class IdentifierControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public IdentifierControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
var style = Resources.Load<StyleSheet>("Styles/Controls/IdentifierControlView");
|
||||
if (style) styleSheets.Add(style);
|
||||
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(string))
|
||||
throw new ArgumentException("Property must be of type string.", "propertyInfo");
|
||||
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var strField = new IdentifierField() { value = (string)m_PropertyInfo.GetValue(m_Node, null) };
|
||||
strField.RegisterValueChangedCallback(OnChange);
|
||||
Add(strField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<string> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Identifier Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class IntegerControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public IntegerControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new IntegerControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class IntegerControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public IntegerControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/IntegerControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(int))
|
||||
throw new ArgumentException("Property must be of type integer.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var intField = new IntegerField { value = (int)m_PropertyInfo.GetValue(m_Node, null) };
|
||||
intField.RegisterValueChangedCallback(OnChange);
|
||||
|
||||
Add(intField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<int> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Integer Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ObjectControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public ObjectControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ObjectControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public ObjectControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!typeof(Object).IsAssignableFrom(propertyInfo.PropertyType))
|
||||
throw new ArgumentException("Property must be assignable to UnityEngine.Object.");
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
label = label ?? propertyInfo.Name;
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label {text = label});
|
||||
|
||||
var value = (Object)m_PropertyInfo.GetValue(m_Node, null);
|
||||
var objectField = new ObjectField { objectType = propertyInfo.PropertyType, value = value };
|
||||
objectField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(objectField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<Object> evt)
|
||||
{
|
||||
var value = (Object)m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (evt.newValue != value)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change + " + m_Node.name);
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class PopupControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
//string[] m_Entries;
|
||||
|
||||
public PopupControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
//m_Entries = entries;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new PopupControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
struct PopupList
|
||||
{
|
||||
public int selectedEntry;
|
||||
public string[] popupEntries;
|
||||
|
||||
public PopupList(string[] entries, int defaultEntry)
|
||||
{
|
||||
popupEntries = entries;
|
||||
selectedEntry = defaultEntry;
|
||||
}
|
||||
}
|
||||
|
||||
class PopupControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
PopupField<string> m_PopupField;
|
||||
|
||||
public PopupControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/PopupControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
|
||||
Type type = propertyInfo.PropertyType;
|
||||
if (type != typeof(PopupList))
|
||||
{
|
||||
throw new ArgumentException("Property must be a PopupList.", "propertyInfo");
|
||||
}
|
||||
|
||||
Add(new Label(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name)));
|
||||
var value = (PopupList)propertyInfo.GetValue(m_Node, null);
|
||||
m_PopupField = new PopupField<string>(new List<string>(value.popupEntries), value.selectedEntry);
|
||||
m_PopupField.RegisterValueChangedCallback(OnValueChanged);
|
||||
Add(m_PopupField);
|
||||
}
|
||||
|
||||
void OnValueChanged(ChangeEvent<string> evt)
|
||||
{
|
||||
var value = (PopupList)m_PropertyInfo.GetValue(m_Node, null);
|
||||
value.selectedEntry = m_PopupField.index;
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using System.Globalization;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class SliderControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
bool m_DisplayMinMax;
|
||||
|
||||
public SliderControlAttribute(string label = null, bool displayMinMax = false)
|
||||
{
|
||||
m_Label = label;
|
||||
m_DisplayMinMax = displayMinMax;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new SliderControlView(m_Label, m_DisplayMinMax, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class SliderControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
bool m_DisplayMinMax;
|
||||
Vector3 m_Value;
|
||||
|
||||
VisualElement m_SliderPanel;
|
||||
Slider m_Slider;
|
||||
FloatField m_SliderInput;
|
||||
FloatField m_MinField;
|
||||
FloatField m_MaxField;
|
||||
|
||||
public SliderControlView(string label, bool displayMinMax, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/SliderControlView"));
|
||||
m_DisplayMinMax = displayMinMax;
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(Vector3))
|
||||
throw new ArgumentException("Property must be of type Vector3.", "propertyInfo");
|
||||
new GUIContent(label ?? ObjectNames.NicifyVariableName(propertyInfo.Name));
|
||||
m_Value = (Vector3)m_PropertyInfo.GetValue(m_Node, null);
|
||||
|
||||
m_Slider = new Slider(m_Value.y, m_Value.z) { value = m_Value.x };
|
||||
m_Slider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue));
|
||||
|
||||
m_SliderInput = new FloatField { value = m_Value.x };
|
||||
m_SliderInput.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
var value = (float)evt.newValue;
|
||||
m_Value.x = value;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
m_SliderInput.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
float minValue = Mathf.Min(m_Value.x, m_Value.y);
|
||||
float maxValue = Mathf.Max(m_Value.x, m_Value.z);
|
||||
m_Value = new Vector3(m_Value.x, minValue, maxValue);
|
||||
m_MinField.value = minValue;
|
||||
m_MaxField.value = maxValue;
|
||||
UpdateSlider();
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
|
||||
m_SliderPanel = new VisualElement { name = "SliderPanel" };
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
m_SliderPanel.Add(new Label(label));
|
||||
|
||||
m_SliderPanel.Add(m_Slider);
|
||||
m_SliderPanel.Add(m_SliderInput);
|
||||
Add(m_SliderPanel);
|
||||
|
||||
if (m_DisplayMinMax)
|
||||
{
|
||||
var fieldsPanel = new VisualElement { name = "FieldsPanel" };
|
||||
m_MinField = AddMinMaxField(fieldsPanel, "Min", 1);
|
||||
m_MaxField = AddMinMaxField(fieldsPanel, "Max", 2);
|
||||
Add(fieldsPanel);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
if (scope == ModificationScope.Graph)
|
||||
{
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeSlider(float newValue)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Slider Change");
|
||||
var value = (Vector3)m_PropertyInfo.GetValue(m_Node, null);
|
||||
value.x = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
if (m_SliderInput != null)
|
||||
m_SliderInput.value = newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void UpdateSlider()
|
||||
{
|
||||
m_SliderPanel.Remove(m_Slider);
|
||||
m_Slider = new Slider(m_Value.y, m_Value.z) { value = m_Value.x };
|
||||
m_Slider.RegisterValueChangedCallback((evt) => OnChangeSlider(evt.newValue));
|
||||
m_SliderPanel.Add(m_Slider);
|
||||
m_SliderPanel.Add(m_SliderInput);
|
||||
}
|
||||
|
||||
FloatField AddMinMaxField(VisualElement panel, string label, int index)
|
||||
{
|
||||
var floatField = new FloatField { value = m_Value[index] };
|
||||
if (label != null)
|
||||
{
|
||||
var labelField = new Label(label);
|
||||
panel.Add(labelField);
|
||||
}
|
||||
|
||||
floatField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
m_Value[index] = (float)evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
floatField.Q("unity-text-input").RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
if (index == 1)
|
||||
{
|
||||
m_Value[index] = Mathf.Min(m_Value[index], m_Value.z);
|
||||
m_MinField.value = m_Value[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Value[index] = Mathf.Max(m_Value[index], m_Value.y);
|
||||
m_MaxField.value = m_Value[index];
|
||||
}
|
||||
float newValue = Mathf.Max(Mathf.Min(m_Value.x, m_Value.z), m_Value.y);
|
||||
m_Value.x = newValue;
|
||||
m_SliderInput.value = newValue;
|
||||
UpdateSlider();
|
||||
m_PropertyInfo.SetValue(m_Node, m_Value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
|
||||
panel.Add(floatField);
|
||||
return floatField;
|
||||
}
|
||||
|
||||
void Repaint<T>(MouseEventBase<T> evt) where T : MouseEventBase<T>, new()
|
||||
{
|
||||
evt.StopPropagation();
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class Texture3DControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public Texture3DControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new Texture3DControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class Texture3DControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public Texture3DControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Texture3D))
|
||||
throw new ArgumentException("Property must be of type Texture 3D.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var textureField = new ObjectField { value = (Texture3D)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Texture3D) };
|
||||
textureField.RegisterValueChangedCallback(OnChange);
|
||||
Add(textureField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Texture Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class TextureArrayControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public TextureArrayControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new TextureArrayControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class TextureArrayControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public TextureArrayControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Texture2DArray))
|
||||
throw new ArgumentException("Property must be of type Texture 2D Array.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var textureField = new ObjectField { value = (Texture2DArray)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Texture2DArray) };
|
||||
textureField.RegisterValueChangedCallback(OnChange);
|
||||
Add(textureField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Texture Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class TextureControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public TextureControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new TextureControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class TextureControlView : VisualElement
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
public TextureControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
if (propertyInfo.PropertyType != typeof(Texture))
|
||||
throw new ArgumentException("Property must be of type Texture.", "propertyInfo");
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
var textureField = new ObjectField { value = (Texture)m_PropertyInfo.GetValue(m_Node, null), objectType = typeof(Texture) };
|
||||
textureField.RegisterValueChangedCallback(OnChange);
|
||||
Add(textureField);
|
||||
}
|
||||
|
||||
void OnChange(ChangeEvent<UnityEngine.Object> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Texture Change");
|
||||
m_PropertyInfo.SetValue(m_Node, evt.newValue, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[Serializable]
|
||||
struct ToggleData
|
||||
{
|
||||
public bool isOn;
|
||||
public bool isEnabled;
|
||||
|
||||
public ToggleData(bool on, bool enabled)
|
||||
{
|
||||
isOn = on;
|
||||
isEnabled = enabled;
|
||||
}
|
||||
|
||||
public ToggleData(bool on)
|
||||
{
|
||||
isOn = on;
|
||||
isEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class ToggleControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
|
||||
public ToggleControlAttribute(string label = null)
|
||||
{
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
return new ToggleControlView(m_Label, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleControlView : VisualElement, AbstractMaterialNodeModificationListener
|
||||
{
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
|
||||
Label m_Label;
|
||||
Toggle m_Toggle;
|
||||
|
||||
public ToggleControlView(string label, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/ToggleControlView"));
|
||||
|
||||
if (propertyInfo.PropertyType != typeof(ToggleData))
|
||||
throw new ArgumentException("Property must be a Toggle.", "propertyInfo");
|
||||
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
|
||||
var value = (ToggleData)m_PropertyInfo.GetValue(m_Node, null);
|
||||
var panel = new VisualElement { name = "togglePanel" };
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
{
|
||||
m_Label = new Label(label);
|
||||
m_Label.SetEnabled(value.isEnabled);
|
||||
panel.Add(m_Label);
|
||||
}
|
||||
|
||||
m_Toggle = new Toggle();
|
||||
m_Toggle.OnToggleChanged(OnChangeToggle);
|
||||
m_Toggle.SetEnabled(value.isEnabled);
|
||||
m_Toggle.value = value.isOn;
|
||||
panel.Add(m_Toggle);
|
||||
Add(panel);
|
||||
}
|
||||
|
||||
public void OnNodeModified(ModificationScope scope)
|
||||
{
|
||||
var value = (ToggleData)m_PropertyInfo.GetValue(m_Node, null);
|
||||
m_Toggle.SetEnabled(value.isEnabled);
|
||||
if (m_Label != null)
|
||||
m_Label.SetEnabled(value.isEnabled);
|
||||
|
||||
if (scope == ModificationScope.Graph)
|
||||
{
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeToggle(ChangeEvent<bool> evt)
|
||||
{
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Toggle Change");
|
||||
var value = (ToggleData)m_PropertyInfo.GetValue(m_Node, null);
|
||||
value.isOn = evt.newValue;
|
||||
m_PropertyInfo.SetValue(m_Node, value, null);
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Controls
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
class MultiFloatControlAttribute : Attribute, IControlAttribute
|
||||
{
|
||||
string m_Label;
|
||||
string m_SubLabel1;
|
||||
string m_SubLabel2;
|
||||
string m_SubLabel3;
|
||||
string m_SubLabel4;
|
||||
|
||||
public MultiFloatControlAttribute(string label = null, string subLabel1 = "X", string subLabel2 = "Y", string subLabel3 = "Z", string subLabel4 = "W")
|
||||
{
|
||||
m_SubLabel1 = subLabel1;
|
||||
m_SubLabel2 = subLabel2;
|
||||
m_SubLabel3 = subLabel3;
|
||||
m_SubLabel4 = subLabel4;
|
||||
m_Label = label;
|
||||
}
|
||||
|
||||
public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!MultiFloatControlView.validTypes.Contains(propertyInfo.PropertyType))
|
||||
return null;
|
||||
return new MultiFloatControlView(m_Label, m_SubLabel1, m_SubLabel2, m_SubLabel3, m_SubLabel4, node, propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiFloatControlView : VisualElement
|
||||
{
|
||||
public static Type[] validTypes = { typeof(float), typeof(Vector2), typeof(Vector3), typeof(Vector4) };
|
||||
|
||||
AbstractMaterialNode m_Node;
|
||||
PropertyInfo m_PropertyInfo;
|
||||
Vector4 m_Value;
|
||||
int m_UndoGroup = -1;
|
||||
|
||||
public MultiFloatControlView(string label, string subLabel1, string subLabel2, string subLabel3, string subLabel4, AbstractMaterialNode node, PropertyInfo propertyInfo)
|
||||
{
|
||||
var components = Array.IndexOf(validTypes, propertyInfo.PropertyType) + 1;
|
||||
if (components == -1)
|
||||
throw new ArgumentException("Property must be of type float, Vector2, Vector3 or Vector4.", "propertyInfo");
|
||||
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/Controls/MultiFloatControlView"));
|
||||
m_Node = node;
|
||||
m_PropertyInfo = propertyInfo;
|
||||
|
||||
label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name);
|
||||
if (!string.IsNullOrEmpty(label))
|
||||
Add(new Label(label));
|
||||
|
||||
m_Value = GetValue();
|
||||
AddField(0, subLabel1);
|
||||
if (components > 1)
|
||||
AddField(1, subLabel2);
|
||||
if (components > 2)
|
||||
AddField(2, subLabel3);
|
||||
if (components > 3)
|
||||
AddField(3, subLabel4);
|
||||
}
|
||||
|
||||
void AddField(int index, string subLabel)
|
||||
{
|
||||
var dummy = new VisualElement { name = "dummy" };
|
||||
var label = new Label(subLabel);
|
||||
dummy.Add(label);
|
||||
Add(dummy);
|
||||
var field = new FloatField { userData = index, value = m_Value[index] };
|
||||
var dragger = new FieldMouseDragger<double>(field);
|
||||
dragger.SetDragZone(label);
|
||||
field.RegisterCallback<MouseDownEvent>(Repaint);
|
||||
field.RegisterCallback<MouseMoveEvent>(Repaint);
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
var value = GetValue();
|
||||
value[index] = (float)evt.newValue;
|
||||
SetValue(value);
|
||||
m_UndoGroup = -1;
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
field.Q("unity-text-input").RegisterCallback<InputEvent>(evt =>
|
||||
{
|
||||
if (m_UndoGroup == -1)
|
||||
{
|
||||
m_UndoGroup = Undo.GetCurrentGroup();
|
||||
m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name);
|
||||
}
|
||||
float newValue;
|
||||
if (!float.TryParse(evt.newData, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out newValue))
|
||||
newValue = 0f;
|
||||
var value = GetValue();
|
||||
value[index] = newValue;
|
||||
SetValue(value);
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
field.Q("unity-text-input").RegisterCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Escape && m_UndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(m_UndoGroup);
|
||||
m_UndoGroup = -1;
|
||||
m_Value = GetValue();
|
||||
evt.StopPropagation();
|
||||
}
|
||||
this.MarkDirtyRepaint();
|
||||
});
|
||||
Add(field);
|
||||
}
|
||||
|
||||
object ValueToPropertyType(Vector4 value)
|
||||
{
|
||||
if (m_PropertyInfo.PropertyType == typeof(float))
|
||||
return value.x;
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector2))
|
||||
return (Vector2)value;
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector3))
|
||||
return (Vector3)value;
|
||||
return value;
|
||||
}
|
||||
|
||||
Vector4 GetValue()
|
||||
{
|
||||
var value = m_PropertyInfo.GetValue(m_Node, null);
|
||||
if (m_PropertyInfo.PropertyType == typeof(float))
|
||||
return new Vector4((float)value, 0f, 0f, 0f);
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector2))
|
||||
return (Vector2)value;
|
||||
if (m_PropertyInfo.PropertyType == typeof(Vector3))
|
||||
return (Vector3)value;
|
||||
return (Vector4)value;
|
||||
}
|
||||
|
||||
void SetValue(Vector4 value)
|
||||
{
|
||||
m_PropertyInfo.SetValue(m_Node, ValueToPropertyType(value), null);
|
||||
}
|
||||
|
||||
void Repaint<T>(MouseEventBase<T> evt) where T : MouseEventBase<T>, new()
|
||||
{
|
||||
evt.StopPropagation();
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using Edge = UnityEditor.Experimental.GraphView.Edge;
|
||||
using UnityEditor.Searcher;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class EdgeConnectorListener : IEdgeConnectorListener
|
||||
{
|
||||
readonly GraphData m_Graph;
|
||||
readonly SearchWindowProvider m_SearchWindowProvider;
|
||||
readonly EditorWindow m_editorWindow;
|
||||
|
||||
public EdgeConnectorListener(GraphData graph, SearchWindowProvider searchWindowProvider, EditorWindow editorWindow)
|
||||
{
|
||||
m_Graph = graph;
|
||||
m_SearchWindowProvider = searchWindowProvider;
|
||||
m_editorWindow = editorWindow;
|
||||
}
|
||||
|
||||
public void OnDropOutsidePort(Edge edge, Vector2 position)
|
||||
{
|
||||
var draggedPort = (edge.output != null ? edge.output.edgeConnector.edgeDragHelper.draggedPort : null) ?? (edge.input != null ? edge.input.edgeConnector.edgeDragHelper.draggedPort : null);
|
||||
m_SearchWindowProvider.connectedPort = (ShaderPort)draggedPort;
|
||||
m_SearchWindowProvider.regenerateEntries = true;//need to be sure the entires are relevant to the edge we are dragging
|
||||
SearcherWindow.Show(m_editorWindow, (m_SearchWindowProvider as SearcherProvider).LoadSearchWindow(),
|
||||
item => (m_SearchWindowProvider as SearcherProvider).OnSearcherSelectEntry(item, position),
|
||||
position, null);
|
||||
m_SearchWindowProvider.regenerateEntries = true;//entries no longer necessarily relevant, need to regenerate
|
||||
}
|
||||
|
||||
public void OnDrop(GraphView graphView, Edge edge)
|
||||
{
|
||||
var leftSlot = edge.output.GetSlot();
|
||||
var rightSlot = edge.input.GetSlot();
|
||||
if (leftSlot != null && rightSlot != null)
|
||||
{
|
||||
m_Graph.owner.RegisterCompleteObjectUndo("Connect Edge");
|
||||
m_Graph.Connect(leftSlot.slotReference, rightSlot.slotReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
interface AbstractMaterialNodeModificationListener
|
||||
{
|
||||
void OnNodeModified(ModificationScope scope);
|
||||
}
|
||||
}
|
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.ShaderGraph.Drawing.Views;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector
|
||||
{
|
||||
class InspectorView : GraphSubWindow
|
||||
{
|
||||
readonly List<Type> m_PropertyDrawerList = new List<Type>();
|
||||
|
||||
List<ISelectable> m_CachedSelectionList = new List<ISelectable>();
|
||||
|
||||
// There's persistent data that is stored in the graph settings property drawer that we need to hold onto between interactions
|
||||
IPropertyDrawer m_graphSettingsPropertyDrawer = new GraphDataPropertyDrawer();
|
||||
public override string windowTitle => "Graph Inspector";
|
||||
public override string elementName => "InspectorView";
|
||||
public override string styleName => "InspectorView";
|
||||
public override string UxmlName => "GraphInspector";
|
||||
public override string layoutKey => "UnityEditor.ShaderGraph.InspectorWindow";
|
||||
|
||||
TabbedView m_GraphInspectorView;
|
||||
TabbedView m_NodeSettingsTab;
|
||||
protected VisualElement m_GraphSettingsContainer;
|
||||
protected VisualElement m_NodeSettingsContainer;
|
||||
|
||||
|
||||
void RegisterPropertyDrawer(Type newPropertyDrawerType)
|
||||
{
|
||||
if (typeof(IPropertyDrawer).IsAssignableFrom(newPropertyDrawerType) == false)
|
||||
Debug.Log("Attempted to register a property drawer that doesn't inherit from IPropertyDrawer!");
|
||||
|
||||
var newPropertyDrawerAttribute = newPropertyDrawerType.GetCustomAttribute<SGPropertyDrawerAttribute>();
|
||||
|
||||
if (newPropertyDrawerAttribute != null)
|
||||
{
|
||||
foreach (var existingPropertyDrawerType in m_PropertyDrawerList)
|
||||
{
|
||||
var existingPropertyDrawerAttribute = existingPropertyDrawerType.GetCustomAttribute<SGPropertyDrawerAttribute>();
|
||||
if (newPropertyDrawerAttribute.propertyType.IsSubclassOf(existingPropertyDrawerAttribute.propertyType))
|
||||
{
|
||||
// Derived types need to be at start of list
|
||||
m_PropertyDrawerList.Insert(0, newPropertyDrawerType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingPropertyDrawerAttribute.propertyType.IsSubclassOf(newPropertyDrawerAttribute.propertyType))
|
||||
{
|
||||
// Add new base class type to end of list
|
||||
m_PropertyDrawerList.Add(newPropertyDrawerType);
|
||||
// Shift already added existing type to the beginning of the list
|
||||
m_PropertyDrawerList.Remove(existingPropertyDrawerType);
|
||||
m_PropertyDrawerList.Insert(0, existingPropertyDrawerType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_PropertyDrawerList.Add(newPropertyDrawerType);
|
||||
}
|
||||
else
|
||||
Debug.Log("Attempted to register property drawer: " + newPropertyDrawerType + " that isn't marked up with the SGPropertyDrawer attribute!");
|
||||
}
|
||||
|
||||
public InspectorView(GraphView graphView) : base(graphView)
|
||||
{
|
||||
m_GraphInspectorView = m_MainContainer.Q<TabbedView>("GraphInspectorView");
|
||||
m_GraphSettingsContainer = m_GraphInspectorView.Q<VisualElement>("GraphSettingsContainer");
|
||||
m_NodeSettingsContainer = m_GraphInspectorView.Q<VisualElement>("NodeSettingsContainer");
|
||||
m_ContentContainer.Add(m_GraphInspectorView);
|
||||
|
||||
isWindowScrollable = true;
|
||||
isWindowResizable = true;
|
||||
|
||||
var unregisteredPropertyDrawerTypes = TypeCache.GetTypesDerivedFrom<IPropertyDrawer>().ToList();
|
||||
|
||||
foreach (var type in unregisteredPropertyDrawerTypes)
|
||||
{
|
||||
RegisterPropertyDrawer(type);
|
||||
}
|
||||
|
||||
// By default at startup, show graph settings
|
||||
m_GraphInspectorView.Activate(m_GraphInspectorView.Q<TabButton>("GraphSettingsButton"));
|
||||
|
||||
isWindowScrollable = true;
|
||||
}
|
||||
|
||||
public void InitializeGraphSettings()
|
||||
{
|
||||
ShowGraphSettings_Internal(m_GraphSettingsContainer);
|
||||
}
|
||||
|
||||
// If any of the selected items are no longer selected, inspector requires an update
|
||||
public bool DoesInspectorNeedUpdate()
|
||||
{
|
||||
var needUpdate = !m_CachedSelectionList.SequenceEqual(selection);
|
||||
return needUpdate;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
ShowGraphSettings_Internal(m_GraphSettingsContainer);
|
||||
|
||||
m_NodeSettingsContainer.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var selectable in selection)
|
||||
{
|
||||
if (selectable is IInspectable inspectable)
|
||||
{
|
||||
DrawInspectable(m_NodeSettingsContainer, inspectable);
|
||||
// Anything selectable in the graph (GraphSettings not included) is only ever interacted with through the
|
||||
// Node Settings tab so we can make the assumption they want to see that tab
|
||||
m_GraphInspectorView.Activate(m_GraphInspectorView.Q<TabButton>("NodeSettingsButton"));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Store this for update checks later, copying list deliberately as we dont want a reference
|
||||
m_CachedSelectionList = new List<ISelectable>(selection);
|
||||
|
||||
m_NodeSettingsContainer.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void DrawInspectable(
|
||||
VisualElement outputVisualElement,
|
||||
IInspectable inspectable,
|
||||
IPropertyDrawer propertyDrawerToUse = null)
|
||||
{
|
||||
InspectorUtils.GatherInspectorContent(m_PropertyDrawerList, outputVisualElement, inspectable, TriggerInspectorUpdate, propertyDrawerToUse);
|
||||
}
|
||||
|
||||
void TriggerInspectorUpdate()
|
||||
{
|
||||
Update();
|
||||
}
|
||||
|
||||
// This should be implemented by any inspector class that wants to define its own GraphSettings
|
||||
// which for SG, is a representation of the settings in GraphData
|
||||
protected virtual void ShowGraphSettings_Internal(VisualElement contentContainer)
|
||||
{
|
||||
var graphEditorView = m_GraphView.GetFirstAncestorOfType<GraphEditorView>();
|
||||
if (graphEditorView == null)
|
||||
return;
|
||||
|
||||
contentContainer.Clear();
|
||||
DrawInspectable(contentContainer, (IInspectable)graphView, m_graphSettingsPropertyDrawer);
|
||||
contentContainer.MarkDirtyRepaint();
|
||||
}
|
||||
}
|
||||
|
||||
public static class InspectorUtils
|
||||
{
|
||||
internal static void GatherInspectorContent(
|
||||
List<Type> propertyDrawerList,
|
||||
VisualElement outputVisualElement,
|
||||
IInspectable inspectable,
|
||||
Action propertyChangeCallback,
|
||||
IPropertyDrawer propertyDrawerToUse = null)
|
||||
{
|
||||
var dataObject = inspectable.GetObjectToInspect();
|
||||
if (dataObject == null)
|
||||
throw new NullReferenceException("DataObject returned by Inspectable is null!");
|
||||
|
||||
var properties = inspectable.GetType().GetProperties(BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (properties == null)
|
||||
throw new NullReferenceException("PropertyInfos returned by Inspectable is null!");
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
{
|
||||
var attribute = propertyInfo.GetCustomAttribute<InspectableAttribute>();
|
||||
if (attribute == null)
|
||||
continue;
|
||||
|
||||
var propertyType = propertyInfo.GetGetMethod(true).Invoke(inspectable, new object[] {}).GetType();
|
||||
|
||||
if (IsPropertyTypeHandled(propertyDrawerList, propertyType, out var propertyDrawerTypeToUse))
|
||||
{
|
||||
var propertyDrawerInstance = propertyDrawerToUse ??
|
||||
(IPropertyDrawer)Activator.CreateInstance(propertyDrawerTypeToUse);
|
||||
// Assign the inspector update delegate so any property drawer can trigger an inspector update if it needs it
|
||||
propertyDrawerInstance.inspectorUpdateDelegate = propertyChangeCallback;
|
||||
// Supply any required data to this particular kind of property drawer
|
||||
inspectable.SupplyDataToPropertyDrawer(propertyDrawerInstance, propertyChangeCallback);
|
||||
var propertyGUI = propertyDrawerInstance.DrawProperty(propertyInfo, dataObject, attribute);
|
||||
outputVisualElement.Add(propertyGUI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsPropertyTypeHandled(
|
||||
List<Type> propertyDrawerList,
|
||||
Type typeOfProperty,
|
||||
out Type propertyDrawerToUse)
|
||||
{
|
||||
propertyDrawerToUse = null;
|
||||
|
||||
// Check to see if a property drawer has been registered that handles this type
|
||||
foreach (var propertyDrawerType in propertyDrawerList)
|
||||
{
|
||||
var typeHandledByPropertyDrawer = propertyDrawerType.GetCustomAttribute<SGPropertyDrawerAttribute>();
|
||||
// Numeric types and boolean wrapper types like ToggleData handled here
|
||||
if (typeHandledByPropertyDrawer.propertyType == typeOfProperty)
|
||||
{
|
||||
propertyDrawerToUse = propertyDrawerType;
|
||||
return true;
|
||||
}
|
||||
// Generics and Enumerable types are handled here
|
||||
else if (typeHandledByPropertyDrawer.propertyType.IsAssignableFrom(typeOfProperty))
|
||||
{
|
||||
// Before returning it, check for a more appropriate type further
|
||||
propertyDrawerToUse = propertyDrawerType;
|
||||
return true;
|
||||
}
|
||||
// Enums are weird and need to be handled explicitly as done below as their runtime type isn't the same as System.Enum
|
||||
else if (typeHandledByPropertyDrawer.propertyType == typeOfProperty.BaseType)
|
||||
{
|
||||
propertyDrawerToUse = propertyDrawerType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,227 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.UIElements.StyleSheets;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector
|
||||
{
|
||||
class MasterPreviewView : VisualElement
|
||||
{
|
||||
PreviewManager m_PreviewManager;
|
||||
GraphData m_Graph;
|
||||
|
||||
PreviewRenderData m_PreviewRenderHandle;
|
||||
Image m_PreviewTextureView;
|
||||
|
||||
public Image previewTextureView
|
||||
{
|
||||
get { return m_PreviewTextureView; }
|
||||
}
|
||||
|
||||
Vector2 m_PreviewScrollPosition;
|
||||
ObjectField m_PreviewMeshPicker;
|
||||
|
||||
Mesh m_PreviousMesh;
|
||||
|
||||
bool m_RecalculateLayout;
|
||||
|
||||
ResizeBorderFrame m_PreviewResizeBorderFrame;
|
||||
|
||||
public ResizeBorderFrame previewResizeBorderFrame
|
||||
{
|
||||
get { return m_PreviewResizeBorderFrame; }
|
||||
}
|
||||
|
||||
VisualElement m_Preview;
|
||||
Label m_Title;
|
||||
|
||||
public VisualElement preview
|
||||
{
|
||||
get { return m_Preview; }
|
||||
}
|
||||
|
||||
List<string> m_DoNotShowPrimitives = new List<string>(new string[] {PrimitiveType.Plane.ToString()});
|
||||
|
||||
static Type s_ContextualMenuManipulator = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypesOrNothing()).FirstOrDefault(t => t.FullName == "UnityEngine.UIElements.ContextualMenuManipulator");
|
||||
static Type s_ObjectSelector = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypesOrNothing()).FirstOrDefault(t => t.FullName == "UnityEditor.ObjectSelector");
|
||||
|
||||
public string assetName
|
||||
{
|
||||
get { return m_Title.text; }
|
||||
set { m_Title.text = value; }
|
||||
}
|
||||
|
||||
public MasterPreviewView(PreviewManager previewManager, GraphData graph)
|
||||
{
|
||||
style.overflow = Overflow.Hidden;
|
||||
m_PreviewManager = previewManager;
|
||||
m_Graph = graph;
|
||||
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/MasterPreviewView"));
|
||||
|
||||
m_PreviewRenderHandle = previewManager.masterRenderData;
|
||||
if (m_PreviewRenderHandle != null)
|
||||
{
|
||||
m_PreviewRenderHandle.onPreviewChanged += OnPreviewChanged;
|
||||
}
|
||||
|
||||
var topContainer = new VisualElement() { name = "top" };
|
||||
{
|
||||
m_Title = new Label() { name = "title" };
|
||||
m_Title.text = "Main Preview";
|
||||
|
||||
topContainer.Add(m_Title);
|
||||
}
|
||||
Add(topContainer);
|
||||
|
||||
m_Preview = new VisualElement {name = "middle"};
|
||||
{
|
||||
m_PreviewTextureView = CreatePreview(Texture2D.blackTexture);
|
||||
m_PreviewScrollPosition = new Vector2(0f, 0f);
|
||||
preview.Add(m_PreviewTextureView);
|
||||
preview.AddManipulator(new Scrollable(OnScroll));
|
||||
}
|
||||
Add(preview);
|
||||
|
||||
m_PreviewResizeBorderFrame = new ResizeBorderFrame(this, this) { name = "resizeBorderFrame" };
|
||||
m_PreviewResizeBorderFrame.maintainAspectRatio = true;
|
||||
Add(m_PreviewResizeBorderFrame);
|
||||
|
||||
m_RecalculateLayout = false;
|
||||
this.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
}
|
||||
|
||||
Image CreatePreview(Texture texture)
|
||||
{
|
||||
if (m_PreviewRenderHandle?.texture != null)
|
||||
{
|
||||
texture = m_PreviewRenderHandle.texture;
|
||||
}
|
||||
|
||||
var image = new Image { name = "preview", image = texture };
|
||||
image.AddManipulator(new Draggable(OnMouseDragPreviewMesh, true));
|
||||
image.AddManipulator((IManipulator)Activator.CreateInstance(s_ContextualMenuManipulator, (Action<ContextualMenuPopulateEvent>)BuildContextualMenu));
|
||||
return image;
|
||||
}
|
||||
|
||||
void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
foreach (var primitiveTypeName in Enum.GetNames(typeof(PrimitiveType)))
|
||||
{
|
||||
if (m_DoNotShowPrimitives.Contains(primitiveTypeName))
|
||||
continue;
|
||||
evt.menu.AppendAction(primitiveTypeName, e => ChangePrimitiveMesh(primitiveTypeName), DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
|
||||
evt.menu.AppendAction("Custom Mesh", e => ChangeMeshCustom(), DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
|
||||
void OnPreviewChanged()
|
||||
{
|
||||
m_PreviewTextureView.image = m_PreviewRenderHandle?.texture ?? Texture2D.blackTexture;
|
||||
if (m_PreviewRenderHandle != null && m_PreviewRenderHandle.shaderData.isOutOfDate)
|
||||
m_PreviewTextureView.tintColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
|
||||
else
|
||||
m_PreviewTextureView.tintColor = Color.white;
|
||||
m_PreviewTextureView.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void ChangePrimitiveMesh(string primitiveName)
|
||||
{
|
||||
Mesh changedPrimitiveMesh = Resources.GetBuiltinResource(typeof(Mesh), string.Format("{0}.fbx", primitiveName)) as Mesh;
|
||||
|
||||
ChangeMesh(changedPrimitiveMesh);
|
||||
}
|
||||
|
||||
void ChangeMesh(Mesh mesh)
|
||||
{
|
||||
Mesh changedMesh = mesh;
|
||||
|
||||
m_PreviewManager.UpdateMasterPreview(ModificationScope.Node);
|
||||
|
||||
if (m_Graph.previewData.serializedMesh.mesh != changedMesh)
|
||||
{
|
||||
m_Graph.previewData.rotation = Quaternion.identity;
|
||||
m_PreviewScrollPosition = Vector2.zero;
|
||||
}
|
||||
|
||||
m_Graph.previewData.serializedMesh.mesh = changedMesh;
|
||||
}
|
||||
|
||||
private static EditorWindow Get()
|
||||
{
|
||||
PropertyInfo P = s_ObjectSelector.GetProperty("get", BindingFlags.Public | BindingFlags.Static);
|
||||
return P.GetValue(null, null) as EditorWindow;
|
||||
}
|
||||
|
||||
void OnMeshChanged(Object obj)
|
||||
{
|
||||
var mesh = obj as Mesh;
|
||||
if (mesh == null)
|
||||
mesh = m_PreviousMesh;
|
||||
ChangeMesh(mesh);
|
||||
}
|
||||
|
||||
void ChangeMeshCustom()
|
||||
{
|
||||
MethodInfo ShowMethod = s_ObjectSelector.GetMethod("Show", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, Type.DefaultBinder, new[] {typeof(Object), typeof(Type), typeof(Object), typeof(bool), typeof(List<int>), typeof(Action<Object>), typeof(Action<Object>)}, new ParameterModifier[7]);
|
||||
m_PreviousMesh = m_Graph.previewData.serializedMesh.mesh;
|
||||
ShowMethod.Invoke(Get(), new object[] { null, typeof(Mesh), null, false, null, (Action<Object>)OnMeshChanged, (Action<Object>)OnMeshChanged });
|
||||
}
|
||||
|
||||
void OnGeometryChanged(GeometryChangedEvent evt)
|
||||
{
|
||||
if (m_RecalculateLayout)
|
||||
{
|
||||
WindowDockingLayout dockingLayout = new WindowDockingLayout();
|
||||
dockingLayout.CalculateDockingCornerAndOffset(layout, parent.layout);
|
||||
dockingLayout.ClampToParentWindow();
|
||||
dockingLayout.ApplyPosition(this);
|
||||
m_RecalculateLayout = false;
|
||||
}
|
||||
|
||||
var currentWidth = m_PreviewRenderHandle?.texture != null ? m_PreviewRenderHandle.texture.width : -1;
|
||||
var currentHeight = m_PreviewRenderHandle?.texture != null ? m_PreviewRenderHandle.texture.height : -1;
|
||||
|
||||
var targetWidth = Mathf.Max(1f, m_PreviewTextureView.contentRect.width);
|
||||
var targetHeight = Mathf.Max(1f, m_PreviewTextureView.contentRect.height);
|
||||
|
||||
if (Mathf.Approximately(currentWidth, targetHeight) && Mathf.Approximately(currentHeight, targetWidth))
|
||||
return;
|
||||
|
||||
m_PreviewTextureView.style.width = evt.newRect.width;
|
||||
m_PreviewTextureView.style.height = evt.newRect.height - 40.0f;
|
||||
m_PreviewManager.ResizeMasterPreview(new Vector2(evt.newRect.width, evt.newRect.width));
|
||||
}
|
||||
|
||||
void OnScroll(float scrollValue)
|
||||
{
|
||||
float rescaleAmount = -scrollValue * .03f;
|
||||
m_Graph.previewData.scale = Mathf.Clamp(m_Graph.previewData.scale + rescaleAmount, 0.2f, 5f);
|
||||
|
||||
m_PreviewManager.UpdateMasterPreview(ModificationScope.Node);
|
||||
}
|
||||
|
||||
void OnMouseDragPreviewMesh(Vector2 deltaMouse)
|
||||
{
|
||||
Vector2 previewSize = m_PreviewTextureView.contentRect.size;
|
||||
|
||||
m_PreviewScrollPosition -= deltaMouse * (Event.current.shift ? 3f : 1f) / Mathf.Min(previewSize.x, previewSize.y) * 140f;
|
||||
m_PreviewScrollPosition.y = Mathf.Clamp(m_PreviewScrollPosition.y, -90f, 90f);
|
||||
Quaternion previewRotation = Quaternion.Euler(m_PreviewScrollPosition.y, 0, 0) * Quaternion.Euler(0, m_PreviewScrollPosition.x, 0);
|
||||
m_Graph.previewData.rotation = previewRotation;
|
||||
|
||||
m_PreviewManager.UpdateMasterPreview(ModificationScope.Node);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector
|
||||
{
|
||||
public static class PropertyDrawerUtils
|
||||
{
|
||||
public static Label CreateLabel(string text, int indentLevel = 0, FontStyle fontStyle = FontStyle.Normal)
|
||||
{
|
||||
string label = new string(' ', indentLevel * 4);
|
||||
var labelVisualElement = new Label(label + text);
|
||||
labelVisualElement.style.unityFontStyleAndWeight = fontStyle;
|
||||
return labelVisualElement;
|
||||
}
|
||||
|
||||
public static Label CreateLabel(string text, int indentLevel = 0)
|
||||
{
|
||||
string label = new string(' ', indentLevel * 4);
|
||||
var labelVisualElement = new Label(label + text);
|
||||
return labelVisualElement;
|
||||
}
|
||||
|
||||
internal static void AddDefaultNodeProperties(VisualElement parentElement, AbstractMaterialNode node, Action setNodesAsDirtyCallback, Action updateNodeViewsCallback)
|
||||
{
|
||||
EnumField precisionField = null;
|
||||
if (node.canSetPrecision)
|
||||
{
|
||||
precisionField = new EnumField(node.precision);
|
||||
var propertyRow = new PropertyRow(new Label("Precision"));
|
||||
propertyRow.Add(precisionField, (field) =>
|
||||
{
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue.Equals(node.precision))
|
||||
return;
|
||||
|
||||
setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change precision");
|
||||
node.precision = (Precision)evt.newValue;
|
||||
node.owner.ValidateGraph();
|
||||
updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
});
|
||||
});
|
||||
if (node is Serialization.MultiJsonInternal.UnknownNodeType)
|
||||
precisionField.SetEnabled(false);
|
||||
parentElement.Add(propertyRow);
|
||||
}
|
||||
|
||||
EnumField previewField = null;
|
||||
if (node.hasPreview)
|
||||
{
|
||||
previewField = new EnumField(node.m_PreviewMode);
|
||||
var propertyRow = new PropertyRow(new Label("Preview"));
|
||||
propertyRow.Add(previewField, (field) =>
|
||||
{
|
||||
field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
if (evt.newValue.Equals(node.m_PreviewMode))
|
||||
return;
|
||||
|
||||
setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change preview");
|
||||
node.m_PreviewMode = (PreviewMode)evt.newValue;
|
||||
updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
});
|
||||
});
|
||||
if (node is Serialization.MultiJsonInternal.UnknownNodeType)
|
||||
previewField.SetEnabled(false);
|
||||
parentElement.Add(propertyRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
internal interface IGetNodePropertyDrawerPropertyData
|
||||
{
|
||||
void GetPropertyData(Action setNodesAsDirtyCallback, Action updateNodeViewsCallback);
|
||||
}
|
||||
|
||||
[SGPropertyDrawer(typeof(AbstractMaterialNode))]
|
||||
public class AbstractMaterialNodePropertyDrawer : IPropertyDrawer, IGetNodePropertyDrawerPropertyData
|
||||
{
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
Action m_setNodesAsDirtyCallback;
|
||||
Action m_updateNodeViewsCallback;
|
||||
|
||||
public void GetPropertyData(Action setNodesAsDirtyCallback, Action updateNodeViewsCallback)
|
||||
{
|
||||
m_setNodesAsDirtyCallback = setNodesAsDirtyCallback;
|
||||
m_updateNodeViewsCallback = updateNodeViewsCallback;
|
||||
}
|
||||
|
||||
VisualElement CreateGUI(AbstractMaterialNode node, InspectableAttribute attribute, out VisualElement propertyVisualElement)
|
||||
{
|
||||
VisualElement nodeSettings = new VisualElement();
|
||||
var nameLabel = PropertyDrawerUtils.CreateLabel($"{node.name} Node", 0, FontStyle.Bold);
|
||||
nodeSettings.Add(nameLabel);
|
||||
if (node.sgVersion < node.latestVersion)
|
||||
{
|
||||
var help = HelpBoxRow.TryGetDeprecatedHelpBoxRow($"{node.name} Node", () =>
|
||||
{
|
||||
m_setNodesAsDirtyCallback?.Invoke();
|
||||
node.owner.owner.RegisterCompleteObjectUndo($"Update {node.name} Node");
|
||||
node.ChangeVersion(node.latestVersion);
|
||||
inspectorUpdateDelegate?.Invoke();
|
||||
m_updateNodeViewsCallback?.Invoke();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
});
|
||||
|
||||
if (help != null)
|
||||
{
|
||||
nodeSettings.Insert(0, help);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyDrawerUtils.AddDefaultNodeProperties(nodeSettings, node, m_setNodesAsDirtyCallback, m_updateNodeViewsCallback);
|
||||
|
||||
propertyVisualElement = null;
|
||||
|
||||
return nodeSettings;
|
||||
}
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
(AbstractMaterialNode)actualObject,
|
||||
attribute,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(bool))]
|
||||
class BoolPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(bool newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
bool fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyToggle,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var row = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
// Create and assign toggle as out variable here so that callers can also do additional work with enabling/disabling if needed
|
||||
propertyToggle = new Toggle();
|
||||
row.Add((Toggle)propertyToggle, (toggle) =>
|
||||
{
|
||||
toggle.value = fieldToDraw;
|
||||
});
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
var toggle = (Toggle)propertyToggle;
|
||||
toggle.OnToggleChanged(evt => valueChangedCallback(evt.newValue));
|
||||
}
|
||||
|
||||
row.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return row;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(
|
||||
PropertyInfo propertyInfo,
|
||||
object actualObject,
|
||||
InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newBoolValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newBoolValue}),
|
||||
(bool)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Color))]
|
||||
class ColorPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Color newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Color fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyColorField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var colorField = new ColorField { value = fieldToDraw, showEyeDropper = false, hdr = false };
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
colorField.RegisterValueChangedCallback(evt => { valueChangedCallback((Color)evt.newValue); });
|
||||
}
|
||||
|
||||
propertyColorField = colorField;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyColorField);
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Color)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Cubemap))]
|
||||
class CubemapPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Cubemap newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Cubemap fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyCubemapField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var objectField = new ObjectField { value = fieldToDraw, objectType = typeof(Cubemap)};
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
objectField.RegisterValueChangedCallback(evt => { valueChangedCallback((Cubemap)evt.newValue); });
|
||||
}
|
||||
|
||||
propertyCubemapField = objectField;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyCubemapField);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Cubemap)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(CustomFunctionNode))]
|
||||
public class CustomFunctionNodePropertyDrawer : IPropertyDrawer, IGetNodePropertyDrawerPropertyData
|
||||
{
|
||||
Action m_setNodesAsDirtyCallback;
|
||||
Action m_updateNodeViewsCallback;
|
||||
|
||||
void IGetNodePropertyDrawerPropertyData.GetPropertyData(Action setNodesAsDirtyCallback, Action updateNodeViewsCallback)
|
||||
{
|
||||
m_setNodesAsDirtyCallback = setNodesAsDirtyCallback;
|
||||
m_updateNodeViewsCallback = updateNodeViewsCallback;
|
||||
}
|
||||
|
||||
VisualElement CreateGUI(CustomFunctionNode node, InspectableAttribute attribute,
|
||||
out VisualElement propertyVisualElement)
|
||||
{
|
||||
var propertySheet = new PropertySheet(PropertyDrawerUtils.CreateLabel($"{node.name} Node", 0, FontStyle.Bold));
|
||||
|
||||
PropertyDrawerUtils.AddDefaultNodeProperties(propertySheet, node, m_setNodesAsDirtyCallback, m_updateNodeViewsCallback);
|
||||
|
||||
var inputListView = new ReorderableSlotListView(node, SlotType.Input, true);
|
||||
inputListView.OnAddCallback += list => inspectorUpdateDelegate();
|
||||
inputListView.OnRemoveCallback += list => inspectorUpdateDelegate();
|
||||
inputListView.OnListRecreatedCallback += () => inspectorUpdateDelegate();
|
||||
propertySheet.Add(inputListView);
|
||||
|
||||
var outputListView = new ReorderableSlotListView(node, SlotType.Output, true);
|
||||
outputListView.OnAddCallback += list => inspectorUpdateDelegate();
|
||||
outputListView.OnRemoveCallback += list => inspectorUpdateDelegate();
|
||||
outputListView.OnListRecreatedCallback += () => inspectorUpdateDelegate();
|
||||
propertySheet.Add(outputListView);
|
||||
|
||||
propertySheet.Add(new HlslFunctionView(node));
|
||||
propertyVisualElement = null;
|
||||
return propertySheet;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject,
|
||||
InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
(CustomFunctionNode)actualObject,
|
||||
attribute,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(IEnumerable<string>))]
|
||||
class DropdownPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(int newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
IEnumerable<string> fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement textArrayField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var propertyRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
textArrayField = new PopupField<string>(fieldToDraw.ToList(), 0);
|
||||
propertyRow.Add(textArrayField);
|
||||
var popupField = (PopupField<string>)textArrayField;
|
||||
popupField.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
valueChangedCallback(popupField.index);
|
||||
});
|
||||
propertyRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
|
||||
return propertyRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newSelectedIndex => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newSelectedIndex}),
|
||||
(IEnumerable<string>)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var textArrayField);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Enum))]
|
||||
class EnumPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Enum newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Enum fieldToDraw,
|
||||
string labelName,
|
||||
Enum defaultValue,
|
||||
out VisualElement propertyVisualElement,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var row = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
propertyVisualElement = new EnumField(defaultValue);
|
||||
row.Add((EnumField)propertyVisualElement, (field) =>
|
||||
{
|
||||
field.value = fieldToDraw;
|
||||
});
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
var enumField = (EnumField)propertyVisualElement;
|
||||
enumField.RegisterValueChangedCallback(evt => valueChangedCallback(evt.newValue));
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(
|
||||
PropertyInfo propertyInfo,
|
||||
object actualObject,
|
||||
InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(newEnumValue =>
|
||||
propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newEnumValue}),
|
||||
(Enum)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
(Enum)attribute.defaultValue,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(float))]
|
||||
class FloatPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(float newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
float fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyFloatField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var floatField = new FloatField {label = "X", value = fieldToDraw};
|
||||
floatField.labelElement.style.minWidth = 15;
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
floatField.RegisterValueChangedCallback(evt => { valueChangedCallback((float)evt.newValue); });
|
||||
}
|
||||
|
||||
propertyFloatField = floatField;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyFloatField);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(float)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Gradient))]
|
||||
class GradientPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Gradient newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Gradient fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyGradientField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var objectField = new GradientField { value = fieldToDraw, colorSpace = ColorSpace.Linear};
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
objectField.RegisterValueChangedCallback(evt => { valueChangedCallback((Gradient)evt.newValue); });
|
||||
}
|
||||
|
||||
propertyGradientField = objectField;
|
||||
|
||||
// Any core widgets used by the inspector over and over should come from some kind of factory
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyGradientField);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Gradient)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditorInternal;
|
||||
using UnityEditor.ShaderGraph.Serialization;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(GraphData))]
|
||||
public class GraphDataPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
public delegate void ChangeConcretePrecisionCallback(ConcretePrecision newValue);
|
||||
public delegate void PostTargetSettingsChangedCallback();
|
||||
|
||||
PostTargetSettingsChangedCallback m_postChangeTargetSettingsCallback;
|
||||
ChangeConcretePrecisionCallback m_postChangeConcretePrecisionCallback;
|
||||
|
||||
Dictionary<Target, bool> m_TargetFoldouts = new Dictionary<Target, bool>();
|
||||
|
||||
public void GetPropertyData(
|
||||
PostTargetSettingsChangedCallback postChangeValueCallback,
|
||||
ChangeConcretePrecisionCallback changeConcretePrecisionCallback)
|
||||
{
|
||||
m_postChangeTargetSettingsCallback = postChangeValueCallback;
|
||||
m_postChangeConcretePrecisionCallback = changeConcretePrecisionCallback;
|
||||
}
|
||||
|
||||
VisualElement GetSettings(GraphData graphData, Action onChange)
|
||||
{
|
||||
var element = new VisualElement() { name = "graphSettings" };
|
||||
|
||||
if (graphData.isSubGraph)
|
||||
return element;
|
||||
|
||||
void RegisterActionToUndo(string actionName)
|
||||
{
|
||||
graphData.owner.RegisterCompleteObjectUndo(actionName);
|
||||
}
|
||||
|
||||
// Add Label
|
||||
var targetSettingsLabel = new Label("Target Settings");
|
||||
targetSettingsLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
element.Add(new PropertyRow(targetSettingsLabel));
|
||||
|
||||
var targetList = new ReorderableListView<JsonData<Target>>(
|
||||
graphData.m_ActiveTargets,
|
||||
"Active Targets",
|
||||
false, // disallow reordering (active list is sorted)
|
||||
target => target.value.displayName);
|
||||
|
||||
targetList.GetAddMenuOptions = () => graphData.GetPotentialTargetDisplayNames();
|
||||
|
||||
targetList.OnAddMenuItemCallback +=
|
||||
(list, addMenuOptionIndex, addMenuOption) =>
|
||||
{
|
||||
RegisterActionToUndo("Add Target");
|
||||
graphData.SetTargetActive(addMenuOptionIndex);
|
||||
m_postChangeTargetSettingsCallback();
|
||||
};
|
||||
|
||||
targetList.RemoveItemCallback +=
|
||||
(list, itemIndex) =>
|
||||
{
|
||||
RegisterActionToUndo("Remove Target");
|
||||
graphData.SetTargetInactive(list[itemIndex].value);
|
||||
m_postChangeTargetSettingsCallback();
|
||||
};
|
||||
|
||||
element.Add(targetList);
|
||||
|
||||
// Iterate active TargetImplementations
|
||||
foreach (var target in graphData.activeTargets)
|
||||
{
|
||||
// Ensure enabled state is being tracked and get value
|
||||
bool foldoutActive;
|
||||
if (!m_TargetFoldouts.TryGetValue(target, out foldoutActive))
|
||||
{
|
||||
foldoutActive = true;
|
||||
m_TargetFoldouts.Add(target, foldoutActive);
|
||||
}
|
||||
|
||||
// Create foldout
|
||||
var foldout = new Foldout() { text = target.displayName, value = foldoutActive, name = "foldout" };
|
||||
element.Add(foldout);
|
||||
foldout.AddToClassList("MainFoldout");
|
||||
foldout.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
// Update foldout value and rebuild
|
||||
m_TargetFoldouts[target] = evt.newValue;
|
||||
foldout.value = evt.newValue;
|
||||
onChange();
|
||||
});
|
||||
|
||||
if (foldout.value)
|
||||
{
|
||||
// Get settings for Target
|
||||
var context = new TargetPropertyGUIContext();
|
||||
target.GetPropertiesGUI(ref context, onChange, RegisterActionToUndo);
|
||||
element.Add(context);
|
||||
}
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
internal VisualElement CreateGUI(GraphData graphData)
|
||||
{
|
||||
var propertySheet = new VisualElement() {name = "graphSettings"};
|
||||
|
||||
if (graphData == null)
|
||||
{
|
||||
Debug.Log("Attempting to draw something that isn't of type GraphData with a GraphDataPropertyDrawer");
|
||||
return propertySheet;
|
||||
}
|
||||
|
||||
{
|
||||
var enumPropertyDrawer = new EnumPropertyDrawer();
|
||||
propertySheet.Add(enumPropertyDrawer.CreateGUI(
|
||||
newValue => { m_postChangeConcretePrecisionCallback((ConcretePrecision)newValue); },
|
||||
graphData.concretePrecision,
|
||||
"Precision",
|
||||
ConcretePrecision.Single,
|
||||
out var propertyVisualElement));
|
||||
}
|
||||
|
||||
if (graphData.isSubGraph)
|
||||
{
|
||||
var enumPropertyDrawer = new EnumPropertyDrawer();
|
||||
propertySheet.Add(enumPropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
graphData.owner.RegisterCompleteObjectUndo("Change Preview Mode");
|
||||
graphData.previewMode = (PreviewMode)newValue;
|
||||
},
|
||||
graphData.previewMode,
|
||||
"Preview",
|
||||
PreviewMode.Inherit,
|
||||
out var propertyVisualElement));
|
||||
}
|
||||
|
||||
propertySheet.Add(GetSettings(graphData, () => this.m_postChangeTargetSettingsCallback()));
|
||||
|
||||
return propertySheet;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI((GraphData)actualObject);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
using static UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers.ShaderInputPropertyDrawer;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
interface IShaderPropertyDrawer
|
||||
{
|
||||
internal void HandlePropertyField(PropertySheet propertySheet, PreChangeValueCallback preChangeValueCallback, PostChangeValueCallback postChangeValueCallback);
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(int))]
|
||||
class IntegerPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(int newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
int fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyFloatField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var integerField = new IntegerField {value = fieldToDraw};
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
integerField.RegisterValueChangedCallback(evt => { valueChangedCallback(evt.newValue); });
|
||||
}
|
||||
|
||||
propertyFloatField = integerField;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyFloatField);
|
||||
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(int)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,372 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Matrix4x4))]
|
||||
class MatrixPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
public enum MatrixDimensions
|
||||
{
|
||||
Two,
|
||||
Three,
|
||||
Four
|
||||
}
|
||||
public MatrixDimensions dimension { get; set; }
|
||||
|
||||
public delegate Vector4 GetMatrixRowDelegate(int rowNumber);
|
||||
|
||||
internal Action PreValueChangeCallback;
|
||||
internal delegate void ValueChangedCallback(Matrix4x4 newValue);
|
||||
internal Action PostValueChangeCallback;
|
||||
// Matrix4x4, Matrix3x3, Matrix2x2 are all value types,
|
||||
// hence the local value doesn't stay up to date after modified
|
||||
// Need a callback to fetch the row data directly from the source
|
||||
internal GetMatrixRowDelegate MatrixRowFetchCallback;
|
||||
|
||||
void HandleMatrix2Property(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
PropertySheet propertySheet,
|
||||
Matrix4x4 matrix2Property,
|
||||
string labelName = "Default")
|
||||
{
|
||||
var vector2PropertyDrawer = new Vector2PropertyDrawer();
|
||||
vector2PropertyDrawer.preValueChangeCallback = PreValueChangeCallback;
|
||||
vector2PropertyDrawer.postValueChangeCallback = PostValueChangeCallback;
|
||||
|
||||
propertySheet.Add(vector2PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector2 row1 = MatrixRowFetchCallback(1);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = newValue.x,
|
||||
m01 = newValue.y,
|
||||
m02 = 0,
|
||||
m03 = 0,
|
||||
m10 = row1.x,
|
||||
m11 = row1.y,
|
||||
m12 = 0,
|
||||
m13 = 0,
|
||||
m20 = 0,
|
||||
m21 = 0,
|
||||
m22 = 0,
|
||||
m23 = 0,
|
||||
m30 = 0,
|
||||
m31 = 0,
|
||||
m32 = 0,
|
||||
m33 = 0,
|
||||
});
|
||||
},
|
||||
matrix2Property.GetRow(0),
|
||||
labelName,
|
||||
out var row0Field
|
||||
));
|
||||
|
||||
propertySheet.Add(vector2PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector2 row0 = MatrixRowFetchCallback(0);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = row0.x,
|
||||
m01 = row0.y,
|
||||
m02 = 0,
|
||||
m03 = 0,
|
||||
m10 = newValue.x,
|
||||
m11 = newValue.y,
|
||||
m12 = 0,
|
||||
m13 = 0,
|
||||
m20 = 0,
|
||||
m21 = 0,
|
||||
m22 = 0,
|
||||
m23 = 0,
|
||||
m30 = 0,
|
||||
m31 = 0,
|
||||
m32 = 0,
|
||||
m33 = 0,
|
||||
});
|
||||
},
|
||||
matrix2Property.GetRow(1),
|
||||
"",
|
||||
out var row1Field
|
||||
));
|
||||
}
|
||||
|
||||
void HandleMatrix3Property(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
PropertySheet propertySheet,
|
||||
Matrix4x4 matrix3Property,
|
||||
string labelName = "Default")
|
||||
{
|
||||
var vector3PropertyDrawer = new Vector3PropertyDrawer();
|
||||
vector3PropertyDrawer.preValueChangeCallback = PreValueChangeCallback;
|
||||
vector3PropertyDrawer.postValueChangeCallback = PostValueChangeCallback;
|
||||
|
||||
propertySheet.Add(vector3PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector3 row1 = MatrixRowFetchCallback(1);
|
||||
Vector3 row2 = MatrixRowFetchCallback(2);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = newValue.x,
|
||||
m01 = newValue.y,
|
||||
m02 = newValue.z,
|
||||
m03 = 0,
|
||||
m10 = row1.x,
|
||||
m11 = row1.y,
|
||||
m12 = row1.z,
|
||||
m13 = 0,
|
||||
m20 = row2.x,
|
||||
m21 = row2.y,
|
||||
m22 = row2.z,
|
||||
m23 = 0,
|
||||
m30 = 0,
|
||||
m31 = 0,
|
||||
m32 = 0,
|
||||
m33 = 0,
|
||||
});
|
||||
},
|
||||
matrix3Property.GetRow(0),
|
||||
labelName,
|
||||
out var row0Field
|
||||
));
|
||||
|
||||
propertySheet.Add(vector3PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector3 row0 = MatrixRowFetchCallback(0);
|
||||
Vector3 row2 = MatrixRowFetchCallback(2);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = row0.x,
|
||||
m01 = row0.y,
|
||||
m02 = row0.z,
|
||||
m03 = 0,
|
||||
m10 = newValue.x,
|
||||
m11 = newValue.y,
|
||||
m12 = newValue.z,
|
||||
m13 = 0,
|
||||
m20 = row2.x,
|
||||
m21 = row2.y,
|
||||
m22 = row2.z,
|
||||
m23 = 0,
|
||||
m30 = 0,
|
||||
m31 = 0,
|
||||
m32 = 0,
|
||||
m33 = 0,
|
||||
});
|
||||
},
|
||||
matrix3Property.GetRow(1),
|
||||
"",
|
||||
out var row1Field
|
||||
));
|
||||
|
||||
propertySheet.Add(vector3PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector3 row0 = MatrixRowFetchCallback(0);
|
||||
Vector3 row1 = MatrixRowFetchCallback(1);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = row0.x,
|
||||
m01 = row0.y,
|
||||
m02 = row0.z,
|
||||
m03 = 0,
|
||||
m10 = row1.x,
|
||||
m11 = row1.y,
|
||||
m12 = row1.z,
|
||||
m13 = 0,
|
||||
m20 = newValue.x,
|
||||
m21 = newValue.y,
|
||||
m22 = newValue.z,
|
||||
m23 = 0,
|
||||
m30 = 0,
|
||||
m31 = 0,
|
||||
m32 = 0,
|
||||
m33 = 0,
|
||||
});
|
||||
},
|
||||
matrix3Property.GetRow(2),
|
||||
"",
|
||||
out var row2Field
|
||||
));
|
||||
}
|
||||
|
||||
void HandleMatrix4Property(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
PropertySheet propertySheet,
|
||||
Matrix4x4 matrix4Property,
|
||||
string labelName = "Default")
|
||||
{
|
||||
var vector4PropertyDrawer = new Vector4PropertyDrawer();
|
||||
vector4PropertyDrawer.preValueChangeCallback = PreValueChangeCallback;
|
||||
vector4PropertyDrawer.postValueChangeCallback = PostValueChangeCallback;
|
||||
|
||||
propertySheet.Add(vector4PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector4 row1 = MatrixRowFetchCallback(1);
|
||||
Vector4 row2 = MatrixRowFetchCallback(2);
|
||||
Vector4 row3 = MatrixRowFetchCallback(3);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = newValue.x,
|
||||
m01 = newValue.y,
|
||||
m02 = newValue.z,
|
||||
m03 = newValue.w,
|
||||
m10 = row1.x,
|
||||
m11 = row1.y,
|
||||
m12 = row1.z,
|
||||
m13 = row1.w,
|
||||
m20 = row2.x,
|
||||
m21 = row2.y,
|
||||
m22 = row2.z,
|
||||
m23 = row2.w,
|
||||
m30 = row3.x,
|
||||
m31 = row3.y,
|
||||
m32 = row3.z,
|
||||
m33 = row3.w,
|
||||
});
|
||||
},
|
||||
matrix4Property.GetRow(0),
|
||||
labelName,
|
||||
out var row0Field
|
||||
));
|
||||
|
||||
propertySheet.Add(vector4PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector4 row0 = MatrixRowFetchCallback(0);
|
||||
Vector4 row2 = MatrixRowFetchCallback(2);
|
||||
Vector4 row3 = MatrixRowFetchCallback(3);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = row0.x,
|
||||
m01 = row0.y,
|
||||
m02 = row0.z,
|
||||
m03 = row0.w,
|
||||
m10 = newValue.x,
|
||||
m11 = newValue.y,
|
||||
m12 = newValue.z,
|
||||
m13 = newValue.w,
|
||||
m20 = row2.x,
|
||||
m21 = row2.y,
|
||||
m22 = row2.z,
|
||||
m23 = row2.w,
|
||||
m30 = row3.x,
|
||||
m31 = row3.y,
|
||||
m32 = row3.z,
|
||||
m33 = row3.w,
|
||||
});
|
||||
},
|
||||
matrix4Property.GetRow(1),
|
||||
"",
|
||||
out var row1Field
|
||||
));
|
||||
|
||||
propertySheet.Add(vector4PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector4 row0 = MatrixRowFetchCallback(0);
|
||||
Vector4 row1 = MatrixRowFetchCallback(1);
|
||||
Vector4 row3 = MatrixRowFetchCallback(3);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = row0.x,
|
||||
m01 = row0.y,
|
||||
m02 = row0.z,
|
||||
m03 = row0.w,
|
||||
m10 = row1.x,
|
||||
m11 = row1.y,
|
||||
m12 = row1.z,
|
||||
m13 = row1.w,
|
||||
m20 = newValue.x,
|
||||
m21 = newValue.y,
|
||||
m22 = newValue.z,
|
||||
m23 = newValue.w,
|
||||
m30 = row3.x,
|
||||
m31 = row3.y,
|
||||
m32 = row3.z,
|
||||
m33 = row3.w,
|
||||
});
|
||||
},
|
||||
matrix4Property.GetRow(2),
|
||||
"",
|
||||
out var row2Field));
|
||||
|
||||
propertySheet.Add(vector4PropertyDrawer.CreateGUI(
|
||||
newValue =>
|
||||
{
|
||||
Vector4 row0 = MatrixRowFetchCallback(0);
|
||||
Vector4 row1 = MatrixRowFetchCallback(1);
|
||||
Vector4 row2 = MatrixRowFetchCallback(2);
|
||||
valueChangedCallback(new Matrix4x4()
|
||||
{
|
||||
m00 = row0.x,
|
||||
m01 = row0.y,
|
||||
m02 = row0.z,
|
||||
m03 = row0.w,
|
||||
m10 = row1.x,
|
||||
m11 = row1.y,
|
||||
m12 = row1.z,
|
||||
m13 = row1.w,
|
||||
m20 = row2.x,
|
||||
m21 = row2.y,
|
||||
m22 = row2.z,
|
||||
m23 = row2.w,
|
||||
m30 = newValue.x,
|
||||
m31 = newValue.y,
|
||||
m32 = newValue.z,
|
||||
m33 = newValue.w,
|
||||
});
|
||||
},
|
||||
matrix4Property.GetRow(3),
|
||||
"",
|
||||
out var row3Field
|
||||
));
|
||||
}
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Matrix4x4 fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyMatrixField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var propertySheet = new PropertySheet();
|
||||
|
||||
switch (dimension)
|
||||
{
|
||||
case MatrixDimensions.Two:
|
||||
HandleMatrix2Property(valueChangedCallback, propertySheet, fieldToDraw, labelName);
|
||||
break;
|
||||
case MatrixDimensions.Three:
|
||||
HandleMatrix3Property(valueChangedCallback, propertySheet, fieldToDraw, labelName);
|
||||
break;
|
||||
case MatrixDimensions.Four:
|
||||
HandleMatrix4Property(valueChangedCallback, propertySheet, fieldToDraw, labelName);
|
||||
break;
|
||||
}
|
||||
|
||||
propertyMatrixField = propertySheet;
|
||||
return propertyMatrixField;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Matrix4x4)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(SampleVirtualTextureNode))]
|
||||
public class SampleVirtualTextureNodePropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
VisualElement CreateGUI(SampleVirtualTextureNode node, InspectableAttribute attribute,
|
||||
out VisualElement propertyVisualElement)
|
||||
{
|
||||
PropertySheet propertySheet = new PropertySheet();
|
||||
|
||||
var enumPropertyDrawer = new EnumPropertyDrawer();
|
||||
propertySheet.Add(enumPropertyDrawer.CreateGUI((newValue) =>
|
||||
{
|
||||
if (node.addressMode == (SampleVirtualTextureNode.AddressMode)newValue)
|
||||
return;
|
||||
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Address Mode Change");
|
||||
node.addressMode = (SampleVirtualTextureNode.AddressMode)newValue;
|
||||
},
|
||||
node.addressMode,
|
||||
"Address Mode",
|
||||
SampleVirtualTextureNode.AddressMode.VtAddressMode_Wrap,
|
||||
out var addressModeVisualElement));
|
||||
|
||||
propertySheet.Add(enumPropertyDrawer.CreateGUI((newValue) =>
|
||||
{
|
||||
if (node.lodCalculation == (SampleVirtualTextureNode.LodCalculation)newValue)
|
||||
return;
|
||||
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Lod Mode Change");
|
||||
node.lodCalculation = (SampleVirtualTextureNode.LodCalculation)newValue;
|
||||
},
|
||||
node.lodCalculation,
|
||||
"Lod Mode",
|
||||
SampleVirtualTextureNode.LodCalculation.VtLevel_Automatic,
|
||||
out var lodCalculationVisualElement));
|
||||
|
||||
propertySheet.Add(enumPropertyDrawer.CreateGUI((newValue) =>
|
||||
{
|
||||
if (node.sampleQuality == (SampleVirtualTextureNode.QualityMode)newValue)
|
||||
return;
|
||||
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Quality Change");
|
||||
node.sampleQuality = (SampleVirtualTextureNode.QualityMode)newValue;
|
||||
},
|
||||
node.sampleQuality,
|
||||
"Quality",
|
||||
SampleVirtualTextureNode.QualityMode.VtSampleQuality_High,
|
||||
out var qualityVisualElement));
|
||||
|
||||
var boolPropertyDrawer = new BoolPropertyDrawer();
|
||||
propertySheet.Add(boolPropertyDrawer.CreateGUI((newValue) =>
|
||||
{
|
||||
if (node.noFeedback == !newValue)
|
||||
return;
|
||||
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Feedback Settings Change");
|
||||
node.noFeedback = !newValue;
|
||||
},
|
||||
!node.noFeedback,
|
||||
"Automatic Streaming",
|
||||
out var propertyToggle));
|
||||
|
||||
// display warning if the current master node doesn't support virtual texturing
|
||||
// TODO: Add warning when no active subTarget supports VT
|
||||
// if (!node.owner.isSubGraph)
|
||||
// {
|
||||
// bool supportedByMasterNode =
|
||||
// node.owner.GetNodes<IMasterNode>().FirstOrDefault()?.supportsVirtualTexturing ?? false;
|
||||
// if (!supportedByMasterNode)
|
||||
// propertySheet.Add(new HelpBoxRow(MessageType.Warning),
|
||||
// (row) => row.Add(new Label(
|
||||
// "The current master node does not support Virtual Texturing, this node will do regular 2D sampling.")));
|
||||
// }
|
||||
|
||||
// display warning if the current render pipeline doesn't support virtual texturing
|
||||
IVirtualTexturingEnabledRenderPipeline vtRp =
|
||||
GraphicsSettings.currentRenderPipeline as IVirtualTexturingEnabledRenderPipeline;
|
||||
if (vtRp == null)
|
||||
propertySheet.Add(new HelpBoxRow(MessageType.Warning),
|
||||
(row) => row.Add(new Label(
|
||||
"The current render pipeline does not support Virtual Texturing, this node will do regular 2D sampling.")));
|
||||
else if (vtRp.virtualTexturingEnabled == false)
|
||||
propertySheet.Add(new HelpBoxRow(MessageType.Warning),
|
||||
(row) => row.Add(new Label(
|
||||
"The current render pipeline has disabled Virtual Texturing, this node will do regular 2D sampling.")));
|
||||
else
|
||||
{
|
||||
#if !ENABLE_VIRTUALTEXTURES
|
||||
propertySheet.Add(new HelpBoxRow(MessageType.Warning),
|
||||
(row) => row.Add(new Label(
|
||||
"Virtual Texturing is disabled globally (possibly by the render pipeline settings), this node will do regular 2D sampling.")));
|
||||
#endif
|
||||
}
|
||||
|
||||
propertyVisualElement = propertySheet;
|
||||
return propertySheet;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject,
|
||||
InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
(SampleVirtualTextureNode)actualObject,
|
||||
attribute,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.ShaderGraph;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(SubGraphOutputNode))]
|
||||
public class SubGraphOutputNodePropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
VisualElement CreateGUI(SubGraphOutputNode node, InspectableAttribute attribute,
|
||||
out VisualElement propertyVisualElement)
|
||||
{
|
||||
var propertySheet = new PropertySheet(PropertyDrawerUtils.CreateLabel($"{node.name} Node", 0, FontStyle.Bold));
|
||||
var inputListView = new ReorderableSlotListView(node, SlotType.Input, false);
|
||||
inputListView.OnAddCallback += list => inspectorUpdateDelegate();
|
||||
inputListView.OnRemoveCallback += list => inspectorUpdateDelegate();
|
||||
inputListView.OnListRecreatedCallback += () => inspectorUpdateDelegate();
|
||||
inputListView.AllowedTypeCallback = SlotValueHelper.AllowedAsSubgraphOutput;
|
||||
propertySheet.Add(inputListView);
|
||||
propertyVisualElement = propertySheet;
|
||||
return propertySheet;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject,
|
||||
InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
(SubGraphOutputNode)actualObject,
|
||||
attribute,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(string))]
|
||||
class TextPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
public TextField textField;
|
||||
public Label label;
|
||||
|
||||
internal delegate void ValueChangedCallback(string newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
string fieldToDraw,
|
||||
string labelName,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
label = PropertyDrawerUtils.CreateLabel(labelName, indentLevel);
|
||||
var propertyRow = new PropertyRow(label);
|
||||
textField = new TextField(512, false, false, ' ') { isDelayed = true };
|
||||
propertyRow.Add(textField,
|
||||
textField =>
|
||||
{
|
||||
textField.value = fieldToDraw;
|
||||
});
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
textField.RegisterValueChangedCallback(evt => valueChangedCallback(evt.newValue));
|
||||
}
|
||||
|
||||
propertyRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return propertyRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newStringValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newStringValue}),
|
||||
(string)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Texture2DArray))]
|
||||
class Texture2DArrayPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Texture2DArray newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Texture2DArray fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyColorField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var objectField = new ObjectField { value = fieldToDraw, objectType = typeof(Texture2DArray)};
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
objectField.RegisterValueChangedCallback(evt => { valueChangedCallback((Texture2DArray)evt.newValue); });
|
||||
}
|
||||
|
||||
propertyColorField = objectField;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyColorField);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Texture2DArray)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Texture))]
|
||||
class Texture2DPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Texture newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Texture fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyColorField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var objectField = new ObjectField { value = fieldToDraw, objectType = typeof(Texture)};
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
objectField.RegisterValueChangedCallback(evt => { valueChangedCallback((Texture)evt.newValue); });
|
||||
}
|
||||
|
||||
propertyColorField = objectField;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyColorField);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Texture)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Texture3D))]
|
||||
class Texture3DPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Texture3D newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Texture fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyColorField,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var objectField = new ObjectField { value = fieldToDraw, objectType = typeof(Texture3D)};
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
objectField.RegisterValueChangedCallback(evt => { valueChangedCallback((Texture3D)evt.newValue); });
|
||||
}
|
||||
|
||||
propertyColorField = objectField;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyColorField);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Texture3D)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(ToggleData))]
|
||||
class ToggleDataPropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(ToggleData newValue);
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
ToggleData fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyToggle,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var row = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
// Create and assign toggle as out variable here so that callers can also do additional work with enabling/disabling if needed
|
||||
propertyToggle = new Toggle();
|
||||
row.Add((Toggle)propertyToggle, (toggle) =>
|
||||
{
|
||||
toggle.value = fieldToDraw.isOn;
|
||||
});
|
||||
|
||||
if (valueChangedCallback != null)
|
||||
{
|
||||
var toggle = (Toggle)propertyToggle;
|
||||
toggle.OnToggleChanged(evt => valueChangedCallback(new ToggleData(evt.newValue)));
|
||||
}
|
||||
|
||||
row.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return row;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(
|
||||
PropertyInfo propertyInfo,
|
||||
object actualObject,
|
||||
InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newBoolValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newBoolValue}),
|
||||
(ToggleData)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Vector2))]
|
||||
class Vector2PropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Vector2 newValue);
|
||||
|
||||
public Action preValueChangeCallback { get; set; }
|
||||
public Action postValueChangeCallback { get; set; }
|
||||
|
||||
EventCallback<KeyDownEvent> m_KeyDownCallback;
|
||||
EventCallback<FocusOutEvent> m_FocusOutCallback;
|
||||
public int mUndoGroup { get; set; } = -1;
|
||||
|
||||
public Vector2PropertyDrawer()
|
||||
{
|
||||
CreateCallbacks();
|
||||
}
|
||||
|
||||
void CreateCallbacks()
|
||||
{
|
||||
m_KeyDownCallback = new EventCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
// Record Undo for input field edit
|
||||
if (mUndoGroup == -1)
|
||||
{
|
||||
mUndoGroup = Undo.GetCurrentGroup();
|
||||
preValueChangeCallback?.Invoke();
|
||||
}
|
||||
// Handle escaping input field edit
|
||||
if (evt.keyCode == KeyCode.Escape && mUndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(mUndoGroup);
|
||||
mUndoGroup = -1;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
// Dont record Undo again until input field is unfocused
|
||||
mUndoGroup++;
|
||||
postValueChangeCallback?.Invoke();
|
||||
});
|
||||
|
||||
m_FocusOutCallback = new EventCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
// Reset UndoGroup when done editing input field
|
||||
mUndoGroup = -1;
|
||||
});
|
||||
}
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Vector2 fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyVec2Field,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var vector2Field = new Vector2Field {value = fieldToDraw};
|
||||
|
||||
var inputFields = vector2Field.Query("unity-text-input").ToList();
|
||||
foreach (var inputField in inputFields)
|
||||
{
|
||||
inputField.RegisterCallback<KeyDownEvent>(m_KeyDownCallback);
|
||||
inputField.RegisterCallback<FocusOutEvent>(m_FocusOutCallback);
|
||||
}
|
||||
// Bind value changed event to callback to handle dragger behavior before actually settings the value
|
||||
vector2Field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
// Only true when setting value via FieldMouseDragger
|
||||
// Undo recorded once per dragger release
|
||||
if (mUndoGroup == -1)
|
||||
preValueChangeCallback?.Invoke();
|
||||
|
||||
valueChangedCallback(evt.newValue);
|
||||
postValueChangeCallback?.Invoke();
|
||||
});
|
||||
|
||||
propertyVec2Field = vector2Field;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyVec2Field);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Vector2)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Vector3))]
|
||||
class Vector3PropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Vector3 newValue);
|
||||
|
||||
public Action preValueChangeCallback { get; set; }
|
||||
public Action postValueChangeCallback { get; set; }
|
||||
|
||||
EventCallback<KeyDownEvent> m_KeyDownCallback;
|
||||
EventCallback<FocusOutEvent> m_FocusOutCallback;
|
||||
public int mUndoGroup { get; set; } = -1;
|
||||
|
||||
void CreateCallbacks()
|
||||
{
|
||||
m_KeyDownCallback = new EventCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
// Record Undo for input field edit
|
||||
if (mUndoGroup == -1)
|
||||
{
|
||||
mUndoGroup = Undo.GetCurrentGroup();
|
||||
preValueChangeCallback?.Invoke();
|
||||
}
|
||||
// Handle escaping input field edit
|
||||
if (evt.keyCode == KeyCode.Escape && mUndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(mUndoGroup);
|
||||
mUndoGroup = -1;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
// Dont record Undo again until input field is unfocused
|
||||
mUndoGroup++;
|
||||
postValueChangeCallback?.Invoke();
|
||||
});
|
||||
|
||||
m_FocusOutCallback = new EventCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
// Reset UndoGroup when done editing input field
|
||||
mUndoGroup = -1;
|
||||
});
|
||||
}
|
||||
|
||||
public Vector3PropertyDrawer()
|
||||
{
|
||||
CreateCallbacks();
|
||||
}
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Vector3 fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyVec3Field,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var vector3Field = new Vector3Field {value = fieldToDraw};
|
||||
|
||||
var inputFields = vector3Field.Query("unity-text-input").ToList();
|
||||
foreach (var inputField in inputFields)
|
||||
{
|
||||
inputField.RegisterCallback<KeyDownEvent>(m_KeyDownCallback);
|
||||
inputField.RegisterCallback<FocusOutEvent>(m_FocusOutCallback);
|
||||
}
|
||||
|
||||
vector3Field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
// Only true when setting value via FieldMouseDragger
|
||||
// Undo recorded once per dragger release
|
||||
if (mUndoGroup == -1)
|
||||
preValueChangeCallback?.Invoke();
|
||||
|
||||
valueChangedCallback(evt.newValue);
|
||||
postValueChangeCallback?.Invoke();
|
||||
});
|
||||
|
||||
propertyVec3Field = vector3Field;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyVec3Field);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Vector3)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers
|
||||
{
|
||||
[SGPropertyDrawer(typeof(Vector4))]
|
||||
class Vector4PropertyDrawer : IPropertyDrawer
|
||||
{
|
||||
internal delegate void ValueChangedCallback(Vector4 newValue);
|
||||
|
||||
public Action preValueChangeCallback { get; set; }
|
||||
public Action postValueChangeCallback { get; set; }
|
||||
|
||||
EventCallback<KeyDownEvent> m_KeyDownCallback;
|
||||
EventCallback<FocusOutEvent> m_FocusOutCallback;
|
||||
public int mUndoGroup { get; set; } = -1;
|
||||
|
||||
void CreateCallbacks()
|
||||
{
|
||||
m_KeyDownCallback = new EventCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
// Record Undo for input field edit
|
||||
if (mUndoGroup == -1)
|
||||
{
|
||||
mUndoGroup = Undo.GetCurrentGroup();
|
||||
preValueChangeCallback?.Invoke();
|
||||
}
|
||||
// Handle escaping input field edit
|
||||
if (evt.keyCode == KeyCode.Escape && mUndoGroup > -1)
|
||||
{
|
||||
Undo.RevertAllDownToGroup(mUndoGroup);
|
||||
mUndoGroup = -1;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
// Dont record Undo again until input field is unfocused
|
||||
mUndoGroup++;
|
||||
postValueChangeCallback?.Invoke();
|
||||
});
|
||||
|
||||
m_FocusOutCallback = new EventCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
// Reset UndoGroup when done editing input field
|
||||
mUndoGroup = -1;
|
||||
});
|
||||
}
|
||||
|
||||
public Vector4PropertyDrawer()
|
||||
{
|
||||
CreateCallbacks();
|
||||
}
|
||||
|
||||
internal VisualElement CreateGUI(
|
||||
ValueChangedCallback valueChangedCallback,
|
||||
Vector4 fieldToDraw,
|
||||
string labelName,
|
||||
out VisualElement propertyVec4Field,
|
||||
int indentLevel = 0)
|
||||
{
|
||||
var vector4Field = new Vector4Field {value = fieldToDraw};
|
||||
|
||||
var inputFields = vector4Field.Query("unity-text-input").ToList();
|
||||
foreach (var inputField in inputFields)
|
||||
{
|
||||
inputField.RegisterCallback<KeyDownEvent>(m_KeyDownCallback);
|
||||
inputField.RegisterCallback<FocusOutEvent>(m_FocusOutCallback);
|
||||
}
|
||||
|
||||
vector4Field.RegisterValueChangedCallback(evt =>
|
||||
{
|
||||
// Only true when setting value via FieldMouseDragger
|
||||
// Undo recorded once per dragger release
|
||||
if (mUndoGroup == -1)
|
||||
preValueChangeCallback?.Invoke();
|
||||
|
||||
valueChangedCallback(evt.newValue);
|
||||
postValueChangeCallback?.Invoke();
|
||||
});
|
||||
|
||||
propertyVec4Field = vector4Field;
|
||||
|
||||
var defaultRow = new PropertyRow(PropertyDrawerUtils.CreateLabel(labelName, indentLevel));
|
||||
defaultRow.Add(propertyVec4Field);
|
||||
defaultRow.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
return defaultRow;
|
||||
}
|
||||
|
||||
public Action inspectorUpdateDelegate { get; set; }
|
||||
|
||||
public VisualElement DrawProperty(PropertyInfo propertyInfo, object actualObject, InspectableAttribute attribute)
|
||||
{
|
||||
return this.CreateGUI(
|
||||
// Use the setter from the provided property as the callback
|
||||
newValue => propertyInfo.GetSetMethod(true).Invoke(actualObject, new object[] {newValue}),
|
||||
(Vector4)propertyInfo.GetValue(actualObject),
|
||||
attribute.labelName,
|
||||
out var propertyVisualElement);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
public class TabButton : VisualElement
|
||||
{
|
||||
internal new class UxmlFactory : UxmlFactory<TabButton, UxmlTraits> {}
|
||||
|
||||
internal new class UxmlTraits : VisualElement.UxmlTraits
|
||||
{
|
||||
private readonly UxmlStringAttributeDescription m_Text = new UxmlStringAttributeDescription { name = "text" };
|
||||
private readonly UxmlStringAttributeDescription m_Target = new UxmlStringAttributeDescription { name = "target" };
|
||||
|
||||
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
|
||||
{
|
||||
base.Init(ve, bag, cc);
|
||||
TabButton item = ve as TabButton;
|
||||
|
||||
item.m_Label.text = m_Text.GetValueFromBag(bag, cc);
|
||||
item.TargetId = m_Target.GetValueFromBag(bag, cc);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly string styleName = "TabButtonStyles";
|
||||
static readonly string UxmlName = "TabButton";
|
||||
static readonly string s_UssClassName = "unity-tab-button";
|
||||
static readonly string s_UssActiveClassName = s_UssClassName + "--active";
|
||||
|
||||
private Label m_Label;
|
||||
|
||||
public bool IsCloseable { get; set; }
|
||||
public string TargetId { get; private set; }
|
||||
public VisualElement Target { get; set; }
|
||||
|
||||
public event Action<TabButton> OnSelect;
|
||||
public event Action<TabButton> OnClose;
|
||||
|
||||
public TabButton()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
public TabButton(string text, VisualElement target)
|
||||
{
|
||||
Init();
|
||||
m_Label.text = text;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
private void PopulateContextMenu(ContextualMenuPopulateEvent populateEvent)
|
||||
{
|
||||
DropdownMenu dropdownMenu = populateEvent.menu;
|
||||
|
||||
if (IsCloseable)
|
||||
{
|
||||
dropdownMenu.AppendAction("Close Tab", e => OnClose(this));
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateContextMenu(VisualElement visualElement)
|
||||
{
|
||||
ContextualMenuManipulator menuManipulator = new ContextualMenuManipulator(PopulateContextMenu);
|
||||
|
||||
visualElement.focusable = true;
|
||||
visualElement.pickingMode = PickingMode.Position;
|
||||
visualElement.AddManipulator(menuManipulator);
|
||||
|
||||
visualElement.AddManipulator(menuManipulator);
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
AddToClassList(s_UssClassName);
|
||||
styleSheets.Add(Resources.Load<StyleSheet>($"Styles/{styleName}"));
|
||||
|
||||
VisualTreeAsset visualTree = Resources.Load<VisualTreeAsset>($"UXML/{UxmlName}");
|
||||
visualTree.CloneTree(this);
|
||||
|
||||
m_Label = this.Q<Label>("Label");
|
||||
|
||||
CreateContextMenu(this);
|
||||
|
||||
RegisterCallback<MouseDownEvent>(OnMouseDownEvent);
|
||||
}
|
||||
|
||||
public void Select()
|
||||
{
|
||||
AddToClassList(s_UssActiveClassName);
|
||||
|
||||
if (Target != null)
|
||||
{
|
||||
Target.style.display = DisplayStyle.Flex;
|
||||
Target.style.flexGrow = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Deselect()
|
||||
{
|
||||
RemoveFromClassList(s_UssActiveClassName);
|
||||
MarkDirtyRepaint();
|
||||
|
||||
if (Target != null)
|
||||
{
|
||||
Target.style.display = DisplayStyle.None;
|
||||
Target.style.flexGrow = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseDownEvent(MouseDownEvent e)
|
||||
{
|
||||
switch (e.button)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
OnSelect?.Invoke(this);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2 when IsCloseable:
|
||||
{
|
||||
OnClose?.Invoke(this);
|
||||
break;
|
||||
}
|
||||
} // End of switch.
|
||||
|
||||
e.StopImmediatePropagation();
|
||||
}
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
public class TabbedView : VisualElement
|
||||
{
|
||||
public new class UxmlFactory : UxmlFactory<TabbedView, UxmlTraits> {}
|
||||
|
||||
private const string k_styleName = "TabbedView";
|
||||
private const string s_UssClassName = "unity-tabbed-view";
|
||||
private const string s_ContentContainerClassName = "unity-tabbed-view__content-container";
|
||||
private const string s_TabsContainerClassName = "unity-tabbed-view__tabs-container";
|
||||
|
||||
private readonly VisualElement m_TabContent;
|
||||
private readonly VisualElement m_Content;
|
||||
|
||||
private readonly List<TabButton> m_Tabs = new List<TabButton>();
|
||||
private TabButton m_ActiveTab;
|
||||
|
||||
public override VisualElement contentContainer => m_Content;
|
||||
|
||||
public TabbedView()
|
||||
{
|
||||
AddToClassList(s_UssClassName);
|
||||
|
||||
styleSheets.Add(Resources.Load<StyleSheet>($"Styles/{k_styleName}"));
|
||||
|
||||
m_TabContent = new VisualElement();
|
||||
m_TabContent.name = "unity-tabs-container";
|
||||
m_TabContent.AddToClassList(s_TabsContainerClassName);
|
||||
hierarchy.Add(m_TabContent);
|
||||
|
||||
m_Content = new VisualElement();
|
||||
m_Content.name = "unity-content-container";
|
||||
m_Content.AddToClassList(s_ContentContainerClassName);
|
||||
hierarchy.Add(m_Content);
|
||||
|
||||
RegisterCallback<AttachToPanelEvent>(ProcessEvent);
|
||||
}
|
||||
|
||||
public void AddTab(TabButton tabButton, bool activate)
|
||||
{
|
||||
m_Tabs.Add(tabButton);
|
||||
m_TabContent.Add(tabButton);
|
||||
|
||||
tabButton.OnClose += RemoveTab;
|
||||
tabButton.OnSelect += Activate;
|
||||
|
||||
if (activate)
|
||||
{
|
||||
Activate(tabButton);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTab(TabButton tabButton)
|
||||
{
|
||||
int index = m_Tabs.IndexOf(tabButton);
|
||||
|
||||
// If this tab is the active one make sure we deselect it first...
|
||||
if (m_ActiveTab == tabButton)
|
||||
{
|
||||
DeselectTab(tabButton);
|
||||
m_ActiveTab = null;
|
||||
}
|
||||
|
||||
m_Tabs.RemoveAt(index);
|
||||
m_TabContent.Remove(tabButton);
|
||||
|
||||
tabButton.OnClose -= RemoveTab;
|
||||
tabButton.OnSelect -= Activate;
|
||||
|
||||
// If we closed the active tab AND we have any tabs left - active the next valid one...
|
||||
if ((m_ActiveTab == null) && m_Tabs.Any())
|
||||
{
|
||||
int clampedIndex = Mathf.Clamp(index, 0, m_Tabs.Count - 1);
|
||||
TabButton tabToActivate = m_Tabs[clampedIndex];
|
||||
|
||||
Activate(tabToActivate);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessEvent(AttachToPanelEvent e)
|
||||
{
|
||||
// This code takes any existing tab buttons and hooks them into the system...
|
||||
for (int i = 0; i < m_Content.childCount; ++i)
|
||||
{
|
||||
VisualElement element = m_Content[i];
|
||||
|
||||
if (element is TabButton button)
|
||||
{
|
||||
m_Content.Remove(element);
|
||||
|
||||
if (button.Target == null)
|
||||
{
|
||||
string targetId = button.TargetId;
|
||||
|
||||
button.Target = this.Q(targetId);
|
||||
}
|
||||
AddTab(button, false);
|
||||
--i;
|
||||
}
|
||||
else
|
||||
{
|
||||
element.style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if we need to, activate this tab...
|
||||
if (m_ActiveTab != null)
|
||||
{
|
||||
SelectTab(m_ActiveTab);
|
||||
}
|
||||
else if (m_TabContent.childCount > 0)
|
||||
{
|
||||
m_ActiveTab = (TabButton)m_TabContent[0];
|
||||
|
||||
SelectTab(m_ActiveTab);
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectTab(TabButton tabButton)
|
||||
{
|
||||
VisualElement target = tabButton.Target;
|
||||
|
||||
tabButton.Select();
|
||||
Add(target);
|
||||
}
|
||||
|
||||
private void DeselectTab(TabButton tabButton)
|
||||
{
|
||||
VisualElement target = tabButton.Target;
|
||||
|
||||
Remove(target);
|
||||
tabButton.Deselect();
|
||||
}
|
||||
|
||||
public void Activate(TabButton button)
|
||||
{
|
||||
if (m_ActiveTab != null)
|
||||
{
|
||||
DeselectTab(m_ActiveTab);
|
||||
}
|
||||
|
||||
m_ActiveTab = button;
|
||||
SelectTab(m_ActiveTab);
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.UIElements.StyleSheets;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
[Serializable]
|
||||
internal class WindowDockingLayout
|
||||
{
|
||||
[SerializeField]
|
||||
bool m_DockingLeft;
|
||||
|
||||
public bool dockingLeft
|
||||
{
|
||||
get => m_DockingLeft;
|
||||
set => m_DockingLeft = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
bool m_DockingTop;
|
||||
|
||||
public bool dockingTop
|
||||
{
|
||||
get => m_DockingTop;
|
||||
set => m_DockingTop = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
float m_VerticalOffset;
|
||||
|
||||
public float verticalOffset
|
||||
{
|
||||
get => m_VerticalOffset;
|
||||
set => m_VerticalOffset = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
float m_HorizontalOffset;
|
||||
|
||||
public float horizontalOffset
|
||||
{
|
||||
get => m_HorizontalOffset;
|
||||
set => m_HorizontalOffset = value;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
Vector2 m_Size;
|
||||
|
||||
public Vector2 size
|
||||
{
|
||||
get => m_Size;
|
||||
set => m_Size = value;
|
||||
}
|
||||
|
||||
public void CalculateDockingCornerAndOffset(Rect layout, Rect parentLayout)
|
||||
{
|
||||
Vector2 layoutCenter = new Vector2(layout.x + layout.width * .5f, layout.y + layout.height * .5f);
|
||||
layoutCenter /= parentLayout.size;
|
||||
|
||||
m_DockingLeft = layoutCenter.x < .5f;
|
||||
m_DockingTop = layoutCenter.y < .5f;
|
||||
|
||||
if (m_DockingLeft)
|
||||
{
|
||||
m_HorizontalOffset = layout.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HorizontalOffset = parentLayout.width - layout.x - layout.width;
|
||||
}
|
||||
|
||||
if (m_DockingTop)
|
||||
{
|
||||
m_VerticalOffset = layout.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_VerticalOffset = parentLayout.height - layout.y - layout.height;
|
||||
}
|
||||
|
||||
m_Size = layout.size;
|
||||
}
|
||||
|
||||
public void ClampToParentWindow()
|
||||
{
|
||||
m_HorizontalOffset = Mathf.Max(0f, m_HorizontalOffset);
|
||||
m_VerticalOffset = Mathf.Max(0f, m_VerticalOffset);
|
||||
}
|
||||
|
||||
public void ApplyPosition(VisualElement target)
|
||||
{
|
||||
if (dockingLeft)
|
||||
{
|
||||
target.style.right = float.NaN;
|
||||
target.style.left = horizontalOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.style.right = horizontalOffset;
|
||||
target.style.left = float.NaN;
|
||||
}
|
||||
|
||||
if (dockingTop)
|
||||
{
|
||||
target.style.bottom = float.NaN;
|
||||
target.style.top = verticalOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.style.top = float.NaN;
|
||||
target.style.bottom = verticalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplySize(VisualElement target)
|
||||
{
|
||||
target.style.width = size.x;
|
||||
target.style.height = size.y;
|
||||
}
|
||||
|
||||
public Rect GetLayout(Rect parentLayout)
|
||||
{
|
||||
Rect layout = new Rect();
|
||||
|
||||
layout.size = size;
|
||||
|
||||
if (dockingLeft)
|
||||
{
|
||||
layout.x = horizontalOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.x = parentLayout.width - size.x - horizontalOffset;
|
||||
}
|
||||
|
||||
if (dockingTop)
|
||||
{
|
||||
layout.y = verticalOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.y = parentLayout.height - size.y - verticalOffset;
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Interfaces
|
||||
{
|
||||
interface ISGResizable : IResizable
|
||||
{
|
||||
// Depending on the return value, the ElementResizer either allows resizing past parent view edge (like in case of StickyNote) or clamps the size at the edges of parent view (like for GraphSubWindows)
|
||||
bool CanResizePastParentBounds();
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using UnityEditor.ShaderGraph;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
interface ISGControlledElement
|
||||
{
|
||||
void OnControllerChanged(ref SGControllerChangedEvent e);
|
||||
|
||||
void OnControllerEvent(SGControllerEvent e);
|
||||
}
|
||||
|
||||
interface ISGControlledElement<T> : ISGControlledElement where T : SGController
|
||||
{
|
||||
T controller { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class Draggable : MouseManipulator
|
||||
{
|
||||
Action<Vector2> m_Handler;
|
||||
|
||||
bool m_Active;
|
||||
|
||||
bool m_OutputDeltaMovement;
|
||||
|
||||
public Draggable(Action<Vector2> handler, bool outputDeltaMovement = false)
|
||||
{
|
||||
m_Handler = handler;
|
||||
m_Active = false;
|
||||
m_OutputDeltaMovement = outputDeltaMovement;
|
||||
activators.Add(new ManipulatorActivationFilter()
|
||||
{
|
||||
button = MouseButton.LeftMouse
|
||||
});
|
||||
}
|
||||
|
||||
protected override void RegisterCallbacksOnTarget()
|
||||
{
|
||||
target.RegisterCallback(new EventCallback<MouseDownEvent>(OnMouseDown), TrickleDownEnum.NoTrickleDown);
|
||||
target.RegisterCallback(new EventCallback<MouseMoveEvent>(OnMouseMove), TrickleDownEnum.NoTrickleDown);
|
||||
target.RegisterCallback(new EventCallback<MouseUpEvent>(OnMouseUp), TrickleDownEnum.NoTrickleDown);
|
||||
}
|
||||
|
||||
protected override void UnregisterCallbacksFromTarget()
|
||||
{
|
||||
target.UnregisterCallback(new EventCallback<MouseDownEvent>(OnMouseDown), TrickleDownEnum.NoTrickleDown);
|
||||
target.UnregisterCallback(new EventCallback<MouseMoveEvent>(OnMouseMove), TrickleDownEnum.NoTrickleDown);
|
||||
target.UnregisterCallback(new EventCallback<MouseUpEvent>(OnMouseUp), TrickleDownEnum.NoTrickleDown);
|
||||
}
|
||||
|
||||
void OnMouseDown(MouseDownEvent evt)
|
||||
{
|
||||
target.CaptureMouse();
|
||||
m_Active = true;
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
void OnMouseMove(MouseMoveEvent evt)
|
||||
{
|
||||
if (m_Active)
|
||||
{
|
||||
if (m_OutputDeltaMovement)
|
||||
{
|
||||
m_Handler(evt.mouseDelta);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Handler(evt.localMousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseUp(MouseUpEvent evt)
|
||||
{
|
||||
m_Active = false;
|
||||
|
||||
if (target.HasMouseCapture())
|
||||
{
|
||||
target.ReleaseMouse();
|
||||
}
|
||||
|
||||
evt.StopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using UnityEditor.ShaderGraph.Drawing.Interfaces;
|
||||
using UnityEditor.ShaderGraph.Drawing.Views;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class ElementResizer : Manipulator
|
||||
{
|
||||
bool m_IsEnabled = true;
|
||||
public bool isEnabled
|
||||
{
|
||||
get => m_IsEnabled;
|
||||
set => m_IsEnabled = value;
|
||||
}
|
||||
|
||||
public readonly ResizableElement.Resizer direction;
|
||||
|
||||
public readonly VisualElement resizedElement;
|
||||
|
||||
public ElementResizer(VisualElement resizedElement, ResizableElement.Resizer direction)
|
||||
{
|
||||
this.direction = direction;
|
||||
this.resizedElement = resizedElement;
|
||||
}
|
||||
|
||||
protected override void RegisterCallbacksOnTarget()
|
||||
{
|
||||
target.RegisterCallback<MouseDownEvent>(OnMouseDown);
|
||||
target.RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||
}
|
||||
|
||||
protected override void UnregisterCallbacksFromTarget()
|
||||
{
|
||||
target.UnregisterCallback<MouseDownEvent>(OnMouseDown);
|
||||
target.UnregisterCallback<MouseUpEvent>(OnMouseUp);
|
||||
}
|
||||
|
||||
Vector2 m_StartMouse;
|
||||
Vector2 m_StartSize;
|
||||
|
||||
Vector2 m_MinSize;
|
||||
Vector2 m_MaxSize;
|
||||
|
||||
Vector2 m_StartPosition;
|
||||
|
||||
bool m_DragStarted = false;
|
||||
|
||||
void OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
if (!isEnabled)
|
||||
return;
|
||||
|
||||
if (e.button == 0 && e.clickCount == 1)
|
||||
{
|
||||
VisualElement resizedTarget = resizedElement.parent;
|
||||
if (resizedTarget != null)
|
||||
{
|
||||
VisualElement resizedBase = resizedTarget.parent;
|
||||
if (resizedBase != null)
|
||||
{
|
||||
target.RegisterCallback<MouseMoveEvent>(OnMouseMove);
|
||||
e.StopPropagation();
|
||||
target.CaptureMouse();
|
||||
m_StartMouse = resizedBase.WorldToLocal(e.mousePosition);
|
||||
m_StartSize = new Vector2(resizedTarget.resolvedStyle.width, resizedTarget.resolvedStyle.height);
|
||||
m_StartPosition = new Vector2(resizedTarget.resolvedStyle.left, resizedTarget.resolvedStyle.top);
|
||||
|
||||
bool minWidthDefined = resizedTarget.resolvedStyle.minWidth != StyleKeyword.Auto;
|
||||
bool maxWidthDefined = resizedTarget.resolvedStyle.maxWidth != StyleKeyword.None;
|
||||
bool minHeightDefined = resizedTarget.resolvedStyle.minHeight != StyleKeyword.Auto;
|
||||
bool maxHeightDefined = resizedTarget.resolvedStyle.maxHeight != StyleKeyword.None;
|
||||
m_MinSize = new Vector2(
|
||||
minWidthDefined ? resizedTarget.resolvedStyle.minWidth.value : Mathf.NegativeInfinity,
|
||||
minHeightDefined ? resizedTarget.resolvedStyle.minHeight.value : Mathf.NegativeInfinity);
|
||||
m_MaxSize = new Vector2(
|
||||
maxWidthDefined ? resizedTarget.resolvedStyle.maxWidth.value : Mathf.Infinity,
|
||||
maxHeightDefined ? resizedTarget.resolvedStyle.maxHeight.value : Mathf.Infinity);
|
||||
|
||||
m_DragStarted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
if (!isEnabled)
|
||||
return;
|
||||
|
||||
VisualElement resizedTarget = resizedElement.parent;
|
||||
VisualElement resizedBase = resizedTarget.parent;
|
||||
|
||||
// Top left position of the parent visual element
|
||||
var parentRootPosition = resizedBase.worldBound;
|
||||
// Top left of the target visual element for resizing
|
||||
var targetRootPosition = resizedTarget.worldBound;
|
||||
var canResizePastParentBounds = ((ISGResizable)resizedTarget).CanResizePastParentBounds();
|
||||
|
||||
Vector2 mousePos = resizedBase.WorldToLocal(e.mousePosition);
|
||||
|
||||
if (!m_DragStarted)
|
||||
{
|
||||
if (resizedTarget is ISGResizable resizable)
|
||||
resizable.OnStartResize();
|
||||
m_DragStarted = true;
|
||||
}
|
||||
|
||||
if ((direction & ResizableElement.Resizer.Right) != 0)
|
||||
{
|
||||
var newWidth = m_StartSize.x + mousePos.x - m_StartMouse.x;
|
||||
var parentRightBoundary = parentRootPosition.x + resizedBase.layout.width;
|
||||
// Also ensure resizing does not happen past edge of parent views boundaries if the target does not allow it
|
||||
if (!canResizePastParentBounds)
|
||||
{
|
||||
if ((targetRootPosition.x + newWidth) > parentRightBoundary)
|
||||
{
|
||||
var targetToRightBoundaryDelta = parentRightBoundary - targetRootPosition.x;
|
||||
newWidth = targetToRightBoundaryDelta;
|
||||
}
|
||||
var newLayoutLeft = targetRootPosition.x - parentRootPosition.x;
|
||||
// When resizing to right, make sure to calculate and set the target elements Style.left before resizing to ensure correct resizing behavior
|
||||
// If Style.left is NaNpx it results in scaling towards the left
|
||||
// This is due to how the WindowDockingLayout code affects GraphSubWindows
|
||||
resizedTarget.style.left = newLayoutLeft;
|
||||
}
|
||||
|
||||
resizedTarget.style.width = Mathf.Clamp(newWidth, m_MinSize.x, m_MaxSize.x);
|
||||
}
|
||||
else if ((direction & ResizableElement.Resizer.Left) != 0)
|
||||
{
|
||||
float delta = mousePos.x - m_StartMouse.x;
|
||||
|
||||
if (m_StartSize.x - delta < m_MinSize.x)
|
||||
{
|
||||
delta = -m_MinSize.x + m_StartSize.x;
|
||||
}
|
||||
else if (m_StartSize.x - delta > m_MaxSize.x)
|
||||
{
|
||||
delta = -m_MaxSize.x + m_StartSize.x;
|
||||
}
|
||||
|
||||
var newWidth = -delta + m_StartSize.x;
|
||||
var targetToLeftBoundaryDelta = delta + m_StartPosition.x;
|
||||
|
||||
if (!canResizePastParentBounds)
|
||||
{
|
||||
// This ensures that the left side of the resizing target never can get pushed past the parent boundary even if mouse is moving really fast
|
||||
targetToLeftBoundaryDelta = Mathf.Clamp(targetToLeftBoundaryDelta, 2.5f, targetToLeftBoundaryDelta);
|
||||
|
||||
// Clamps width to max out at left edge of parent window
|
||||
if (Mathf.Approximately(targetToLeftBoundaryDelta, 2.5f))
|
||||
newWidth = (m_StartPosition.x + m_StartSize.x);
|
||||
|
||||
newWidth = Mathf.Clamp(newWidth, m_MinSize.x, m_MaxSize.x);
|
||||
}
|
||||
|
||||
resizedTarget.style.left = targetToLeftBoundaryDelta;
|
||||
resizedTarget.style.width = newWidth;
|
||||
}
|
||||
|
||||
if ((direction & ResizableElement.Resizer.Bottom) != 0)
|
||||
{
|
||||
var delta = mousePos.y - m_StartMouse.y;
|
||||
var newHeight = m_StartSize.y + delta;
|
||||
|
||||
var parentBottomBoundary = parentRootPosition.y + resizedBase.layout.height;
|
||||
if (!canResizePastParentBounds)
|
||||
{
|
||||
if ((targetRootPosition.y + newHeight) > parentBottomBoundary)
|
||||
{
|
||||
var targetToBottomBoundaryDelta = parentBottomBoundary - targetRootPosition.y;
|
||||
newHeight = targetToBottomBoundaryDelta;
|
||||
}
|
||||
var targetToTopBoundaryDelta = targetRootPosition.y - parentRootPosition.y;
|
||||
// When resizing to bottom, make sure to calculate and set the target elements Style.top before resizing to ensure correct resizing behavior
|
||||
// If Style.top is NaNpx it results in scaling towards the bottom
|
||||
// This is due to how the WindowDockingLayout code affects GraphSubWindows
|
||||
resizedTarget.style.top = targetToTopBoundaryDelta;
|
||||
|
||||
newHeight = Mathf.Clamp(newHeight, m_MinSize.y, m_MaxSize.y);
|
||||
}
|
||||
|
||||
resizedTarget.style.height = newHeight;
|
||||
}
|
||||
else if ((direction & ResizableElement.Resizer.Top) != 0)
|
||||
{
|
||||
float delta = mousePos.y - m_StartMouse.y;
|
||||
|
||||
if (m_StartSize.y - delta < m_MinSize.y)
|
||||
{
|
||||
delta = -m_MinSize.y + m_StartSize.y;
|
||||
}
|
||||
else if (m_StartSize.y - delta > m_MaxSize.y)
|
||||
{
|
||||
delta = -m_MaxSize.y + m_StartSize.y;
|
||||
}
|
||||
|
||||
var newHeight = -delta + m_StartSize.y;
|
||||
var targetToTopBoundaryDelta = m_StartPosition.y + delta;
|
||||
if (!canResizePastParentBounds)
|
||||
{
|
||||
// This ensures that the top of the resizing target never can get pushed past the parent boundary even if mouse is moving really fast
|
||||
targetToTopBoundaryDelta = Mathf.Clamp(targetToTopBoundaryDelta, 2.5f, targetToTopBoundaryDelta);
|
||||
|
||||
// Clamps height to max out at top edge of parent window
|
||||
if (Mathf.Approximately(targetToTopBoundaryDelta, 2.5f))
|
||||
newHeight = (m_StartPosition.y + m_StartSize.y);
|
||||
|
||||
newHeight = Mathf.Clamp(newHeight, m_MinSize.y, m_MaxSize.y);
|
||||
}
|
||||
|
||||
resizedTarget.style.top = targetToTopBoundaryDelta;
|
||||
resizedTarget.style.height = newHeight;
|
||||
}
|
||||
e.StopPropagation();
|
||||
}
|
||||
|
||||
void OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
if (!isEnabled)
|
||||
return;
|
||||
|
||||
if (e.button == 0)
|
||||
{
|
||||
VisualElement resizedTarget = resizedElement.parent;
|
||||
if (resizedTarget.style.width != m_StartSize.x || resizedTarget.style.height != m_StartSize.y)
|
||||
{
|
||||
if (resizedTarget is ISGResizable resizable)
|
||||
resizable.OnResized();
|
||||
}
|
||||
target.UnregisterCallback<MouseMoveEvent>(OnMouseMove);
|
||||
target.ReleaseMouse();
|
||||
e.StopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
class ResizeBorderFrame : VisualElement
|
||||
{
|
||||
List<ResizeSideHandle> m_ResizeSideHandles;
|
||||
|
||||
bool m_MaintainApsectRatio;
|
||||
|
||||
public bool maintainAspectRatio
|
||||
{
|
||||
get { return m_MaintainApsectRatio; }
|
||||
set
|
||||
{
|
||||
m_MaintainApsectRatio = value;
|
||||
foreach (ResizeSideHandle resizeHandle in m_ResizeSideHandles)
|
||||
{
|
||||
resizeHandle.maintainAspectRatio = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action OnResizeFinished;
|
||||
|
||||
public ResizeBorderFrame(VisualElement target)
|
||||
{
|
||||
InitializeResizeBorderFrame(target, target);
|
||||
}
|
||||
|
||||
public ResizeBorderFrame(VisualElement target, VisualElement container)
|
||||
{
|
||||
InitializeResizeBorderFrame(target, container);
|
||||
}
|
||||
|
||||
void InitializeResizeBorderFrame(VisualElement target, VisualElement container)
|
||||
{
|
||||
pickingMode = PickingMode.Ignore;
|
||||
|
||||
AddToClassList("resizeBorderFrame");
|
||||
|
||||
m_ResizeSideHandles = new List<ResizeSideHandle>();
|
||||
|
||||
// Add resize handles along the border
|
||||
// m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.TopLeft));
|
||||
// m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Top));
|
||||
// m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.TopRight));
|
||||
// m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Right));
|
||||
m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.BottomRight));
|
||||
// m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Bottom));
|
||||
// m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.BottomLeft));
|
||||
// m_ResizeSideHandles.Add(new ResizeSideHandle(target, container, ResizeHandleAnchor.Left));
|
||||
|
||||
foreach (ResizeSideHandle resizeHandle in m_ResizeSideHandles)
|
||||
{
|
||||
resizeHandle.OnResizeFinished += HandleResizefinished;
|
||||
Add(resizeHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleResizefinished()
|
||||
{
|
||||
if (OnResizeFinished != null)
|
||||
{
|
||||
OnResizeFinished();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,365 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.UIElements.StyleSheets;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
enum ResizeDirection
|
||||
{
|
||||
Any,
|
||||
Vertical,
|
||||
Horizontal
|
||||
}
|
||||
|
||||
enum ResizeHandleAnchor
|
||||
{
|
||||
Top,
|
||||
TopRight,
|
||||
Right,
|
||||
BottomRight,
|
||||
Bottom,
|
||||
BottomLeft,
|
||||
Left,
|
||||
TopLeft
|
||||
}
|
||||
|
||||
class ResizeSideHandle : ImmediateModeElement
|
||||
{
|
||||
VisualElement m_ResizeTarget;
|
||||
VisualElement m_Container;
|
||||
|
||||
WindowDockingLayout m_WindowDockingLayout;
|
||||
|
||||
bool m_MaintainAspectRatio;
|
||||
|
||||
public bool maintainAspectRatio
|
||||
{
|
||||
get { return m_MaintainAspectRatio; }
|
||||
set { m_MaintainAspectRatio = value; }
|
||||
}
|
||||
|
||||
public Action OnResizeFinished;
|
||||
|
||||
bool m_Dragging;
|
||||
|
||||
Rect m_ResizeBeginLayout;
|
||||
Vector2 m_ResizeBeginMousePosition;
|
||||
|
||||
private GUIStyle m_StyleWidget;
|
||||
private GUIStyle m_StyleLabel;
|
||||
private Texture image { get; set; }
|
||||
|
||||
public ResizeSideHandle(VisualElement resizeTarget, VisualElement container, ResizeHandleAnchor anchor)
|
||||
{
|
||||
m_WindowDockingLayout = new WindowDockingLayout();
|
||||
|
||||
m_ResizeTarget = resizeTarget;
|
||||
m_Container = container;
|
||||
|
||||
AddToClassList("resize");
|
||||
|
||||
switch (anchor)
|
||||
{
|
||||
case ResizeHandleAnchor.Top:
|
||||
{
|
||||
AddToClassList("vertical");
|
||||
AddToClassList("top");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromTop);
|
||||
break;
|
||||
}
|
||||
case ResizeHandleAnchor.TopRight:
|
||||
{
|
||||
AddToClassList("diagonal");
|
||||
AddToClassList("top-right");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromTopRight);
|
||||
break;
|
||||
}
|
||||
case ResizeHandleAnchor.Right:
|
||||
{
|
||||
AddToClassList("horizontal");
|
||||
AddToClassList("right");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromRight);
|
||||
break;
|
||||
}
|
||||
case ResizeHandleAnchor.BottomRight:
|
||||
{
|
||||
AddToClassList("diagonal");
|
||||
AddToClassList("bottom-right");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromBottomRight);
|
||||
break;
|
||||
}
|
||||
case ResizeHandleAnchor.Bottom:
|
||||
{
|
||||
AddToClassList("vertical");
|
||||
AddToClassList("bottom");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromBottom);
|
||||
break;
|
||||
}
|
||||
case ResizeHandleAnchor.BottomLeft:
|
||||
{
|
||||
AddToClassList("diagonal");
|
||||
AddToClassList("bottom-left");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromBottomLeft);
|
||||
break;
|
||||
}
|
||||
case ResizeHandleAnchor.Left:
|
||||
{
|
||||
AddToClassList("horizontal");
|
||||
AddToClassList("left");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromLeft);
|
||||
break;
|
||||
}
|
||||
case ResizeHandleAnchor.TopLeft:
|
||||
{
|
||||
AddToClassList("diagonal");
|
||||
AddToClassList("top-left");
|
||||
RegisterCallback<MouseMoveEvent>(HandleResizeFromTopLeft);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RegisterCallback<MouseDownEvent>(HandleMouseDown);
|
||||
RegisterCallback<MouseUpEvent>(HandleDraggableMouseUp);
|
||||
|
||||
m_ResizeTarget.RegisterCallback<GeometryChangedEvent>(InitialLayoutSetup);
|
||||
}
|
||||
|
||||
void InitialLayoutSetup(GeometryChangedEvent evt)
|
||||
{
|
||||
m_ResizeTarget.UnregisterCallback<GeometryChangedEvent>(InitialLayoutSetup);
|
||||
}
|
||||
|
||||
void HandleResizeFromTop(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.top = float.NaN;
|
||||
m_Container.style.bottom = m_Container.parent.layout.height - m_Container.layout.yMax;
|
||||
|
||||
float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height - relativeMousePosition.y);
|
||||
|
||||
m_ResizeTarget.style.height = newHeight;
|
||||
|
||||
if (maintainAspectRatio)
|
||||
m_ResizeTarget.style.width = newHeight;
|
||||
|
||||
mouseMoveEvent.StopImmediatePropagation();
|
||||
}
|
||||
|
||||
void HandleResizeFromTopRight(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.top = float.NaN;
|
||||
m_Container.style.bottom = m_Container.parent.layout.height - m_Container.layout.yMax;
|
||||
m_Container.style.left = m_Container.layout.xMin;
|
||||
m_Container.style.right = float.NaN;
|
||||
|
||||
float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width + relativeMousePosition.x);
|
||||
float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height - relativeMousePosition.y);
|
||||
|
||||
if (maintainAspectRatio)
|
||||
newWidth = newHeight = Mathf.Min(newWidth, newHeight);
|
||||
|
||||
m_ResizeTarget.style.width = newWidth;
|
||||
m_ResizeTarget.style.height = newHeight;
|
||||
|
||||
mouseMoveEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleResizeFromRight(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.left = m_Container.layout.xMin;
|
||||
m_Container.style.right = float.NaN;
|
||||
|
||||
float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width + relativeMousePosition.x);
|
||||
|
||||
m_ResizeTarget.style.width = newWidth;
|
||||
|
||||
if (maintainAspectRatio)
|
||||
{
|
||||
m_ResizeTarget.style.height = newWidth;
|
||||
}
|
||||
|
||||
mouseMoveEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleResizeFromBottomRight(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.top = m_Container.layout.yMin;
|
||||
m_Container.style.bottom = float.NaN;
|
||||
m_Container.style.left = m_Container.layout.xMin;
|
||||
m_Container.style.right = float.NaN;
|
||||
|
||||
float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width + relativeMousePosition.x);
|
||||
float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height + relativeMousePosition.y);
|
||||
|
||||
if (maintainAspectRatio)
|
||||
newWidth = newHeight = Mathf.Min(newWidth, newHeight);
|
||||
|
||||
m_ResizeTarget.style.width = newWidth;
|
||||
m_ResizeTarget.style.height = newHeight;
|
||||
|
||||
mouseMoveEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleResizeFromBottom(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.top = m_Container.layout.yMin;
|
||||
m_Container.style.bottom = float.NaN;
|
||||
|
||||
float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height + relativeMousePosition.y);
|
||||
|
||||
m_ResizeTarget.style.height = newHeight;
|
||||
|
||||
if (maintainAspectRatio)
|
||||
m_ResizeTarget.style.width = newHeight;
|
||||
|
||||
mouseMoveEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleResizeFromBottomLeft(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.top = m_Container.layout.yMin;
|
||||
m_Container.style.bottom = float.NaN;
|
||||
m_Container.style.left = float.NaN;
|
||||
m_Container.style.right = m_Container.parent.layout.width - m_Container.layout.xMax;
|
||||
|
||||
float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width - relativeMousePosition.x);
|
||||
float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height + relativeMousePosition.y);
|
||||
|
||||
if (maintainAspectRatio)
|
||||
newWidth = newHeight = Mathf.Min(newWidth, newHeight);
|
||||
|
||||
m_ResizeTarget.style.width = newWidth;
|
||||
m_ResizeTarget.style.height = newHeight;
|
||||
|
||||
mouseMoveEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleResizeFromLeft(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.left = float.NaN;
|
||||
m_Container.style.right = m_Container.parent.layout.width - m_Container.layout.xMax;
|
||||
|
||||
float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width - relativeMousePosition.x);
|
||||
|
||||
m_ResizeTarget.style.width = newWidth;
|
||||
|
||||
if (maintainAspectRatio)
|
||||
m_ResizeTarget.style.height = newWidth;
|
||||
|
||||
mouseMoveEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleResizeFromTopLeft(MouseMoveEvent mouseMoveEvent)
|
||||
{
|
||||
if (!m_Dragging)
|
||||
return;
|
||||
|
||||
Vector2 relativeMousePosition = mouseMoveEvent.mousePosition - m_ResizeBeginMousePosition;
|
||||
|
||||
// Set anchor points for positioning
|
||||
m_Container.style.top = float.NaN;
|
||||
m_Container.style.bottom = m_Container.parent.layout.height - m_Container.layout.yMax;
|
||||
m_Container.style.left = float.NaN;
|
||||
m_Container.style.right = m_Container.parent.layout.width - m_Container.layout.xMax;
|
||||
|
||||
float newWidth = Mathf.Max(0f, m_ResizeBeginLayout.width - relativeMousePosition.x);
|
||||
float newHeight = Mathf.Max(0f, m_ResizeBeginLayout.height - relativeMousePosition.y);
|
||||
|
||||
if (maintainAspectRatio)
|
||||
newWidth = newHeight = Mathf.Min(newWidth, newHeight);
|
||||
|
||||
m_ResizeTarget.style.width = newWidth;
|
||||
m_ResizeTarget.style.height = newHeight;
|
||||
|
||||
mouseMoveEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleMouseDown(MouseDownEvent mouseDownEvent)
|
||||
{
|
||||
// Get the docking settings for the window, as well as the
|
||||
// layout and mouse position when resize begins.
|
||||
m_WindowDockingLayout.CalculateDockingCornerAndOffset(m_Container.layout, m_Container.parent.layout);
|
||||
m_WindowDockingLayout.ApplyPosition(m_Container);
|
||||
|
||||
m_ResizeBeginLayout = m_ResizeTarget.layout;
|
||||
m_ResizeBeginMousePosition = mouseDownEvent.mousePosition;
|
||||
|
||||
m_Dragging = true;
|
||||
this.CaptureMouse();
|
||||
mouseDownEvent.StopPropagation();
|
||||
}
|
||||
|
||||
void HandleDraggableMouseUp(MouseUpEvent mouseUpEvent)
|
||||
{
|
||||
m_Dragging = false;
|
||||
|
||||
if (this.HasMouseCapture())
|
||||
this.ReleaseMouse();
|
||||
|
||||
if (OnResizeFinished != null)
|
||||
OnResizeFinished();
|
||||
|
||||
m_WindowDockingLayout.CalculateDockingCornerAndOffset(m_Container.layout, m_Container.parent.layout);
|
||||
m_WindowDockingLayout.ApplyPosition(m_Container);
|
||||
}
|
||||
|
||||
protected override void ImmediateRepaint()
|
||||
{
|
||||
if (m_StyleWidget == null)
|
||||
{
|
||||
m_StyleWidget = new GUIStyle("WindowBottomResize") { fixedHeight = 0 };
|
||||
image = m_StyleWidget.normal.background;
|
||||
}
|
||||
|
||||
if (image == null)
|
||||
{
|
||||
Debug.LogWarning("null texture passed to GUI.DrawTexture");
|
||||
return;
|
||||
}
|
||||
|
||||
GUI.DrawTexture(contentRect, image, ScaleMode.ScaleAndCrop, true, 0, GUI.color, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class Scrollable : MouseManipulator
|
||||
{
|
||||
Action<float> m_Handler;
|
||||
|
||||
public Scrollable(Action<float> handler)
|
||||
{
|
||||
m_Handler = handler;
|
||||
}
|
||||
|
||||
protected override void RegisterCallbacksOnTarget()
|
||||
{
|
||||
target.RegisterCallback<WheelEvent>(HandleMouseWheelEvent);
|
||||
}
|
||||
|
||||
protected override void UnregisterCallbacksFromTarget()
|
||||
{
|
||||
target.UnregisterCallback<WheelEvent>(HandleMouseWheelEvent);
|
||||
}
|
||||
|
||||
void HandleMouseWheelEvent(WheelEvent evt)
|
||||
{
|
||||
m_Handler(evt.delta.y);
|
||||
evt.StopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.UIElements.StyleSheets;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class WindowDraggable : MouseManipulator
|
||||
{
|
||||
bool m_Active;
|
||||
|
||||
WindowDockingLayout m_WindowDockingLayout;
|
||||
|
||||
Vector2 m_LocalMosueOffset;
|
||||
|
||||
VisualElement m_Handle;
|
||||
GraphView m_GraphView;
|
||||
|
||||
public Action OnDragFinished;
|
||||
|
||||
public WindowDraggable(VisualElement handle = null, VisualElement container = null)
|
||||
{
|
||||
m_Handle = handle;
|
||||
m_Active = false;
|
||||
m_WindowDockingLayout = new WindowDockingLayout();
|
||||
|
||||
if (container != null)
|
||||
container.RegisterCallback<GeometryChangedEvent>(OnParentGeometryChanged);
|
||||
}
|
||||
|
||||
protected override void RegisterCallbacksOnTarget()
|
||||
{
|
||||
if (m_Handle == null)
|
||||
m_Handle = target;
|
||||
m_Handle.RegisterCallback(new EventCallback<MouseDownEvent>(OnMouseDown), TrickleDownEnum.NoTrickleDown);
|
||||
m_Handle.RegisterCallback(new EventCallback<MouseMoveEvent>(OnMouseMove), TrickleDownEnum.NoTrickleDown);
|
||||
m_Handle.RegisterCallback(new EventCallback<MouseUpEvent>(OnMouseUp), TrickleDownEnum.NoTrickleDown);
|
||||
target.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
}
|
||||
|
||||
protected override void UnregisterCallbacksFromTarget()
|
||||
{
|
||||
m_Handle.UnregisterCallback(new EventCallback<MouseDownEvent>(OnMouseDown), TrickleDownEnum.NoTrickleDown);
|
||||
m_Handle.UnregisterCallback(new EventCallback<MouseMoveEvent>(OnMouseMove), TrickleDownEnum.NoTrickleDown);
|
||||
m_Handle.UnregisterCallback(new EventCallback<MouseUpEvent>(OnMouseUp), TrickleDownEnum.NoTrickleDown);
|
||||
target.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
if (m_GraphView != null)
|
||||
m_GraphView.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
}
|
||||
|
||||
void OnMouseDown(MouseDownEvent evt)
|
||||
{
|
||||
m_Active = true;
|
||||
|
||||
VisualElement parent = target.parent;
|
||||
while (parent != null && !(parent is GraphView))
|
||||
parent = parent.parent;
|
||||
m_GraphView = parent as GraphView;
|
||||
|
||||
if (m_GraphView != null)
|
||||
m_GraphView.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
|
||||
|
||||
// m_LocalMouseOffset is offset from the target element's (0, 0) to the
|
||||
// to the mouse position.
|
||||
m_LocalMosueOffset = m_Handle.WorldToLocal(evt.mousePosition);
|
||||
|
||||
m_Handle.CaptureMouse();
|
||||
evt.StopImmediatePropagation();
|
||||
}
|
||||
|
||||
void OnMouseMove(MouseMoveEvent evt)
|
||||
{
|
||||
if (m_Active)
|
||||
{
|
||||
// The mouse position of is corrected according to the offset within the target
|
||||
// element (m_LocalWorldOffset) to set the position relative to the mouse position
|
||||
// when the dragging started.
|
||||
Vector2 position = target.parent.WorldToLocal(evt.mousePosition) - m_LocalMosueOffset;
|
||||
|
||||
// Make sure that the object remains in the parent window
|
||||
position.x = Mathf.Clamp(position.x, 0f, target.parent.layout.width - target.layout.width);
|
||||
position.y = Mathf.Clamp(position.y, 0f, target.parent.layout.height - target.layout.height);
|
||||
|
||||
// While moving, use only the left and top position properties,
|
||||
// while keeping the others NaN to not affect layout.
|
||||
target.style.left = position.x;
|
||||
target.style.top = position.y;
|
||||
target.style.right = float.NaN;
|
||||
target.style.bottom = float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseUp(MouseUpEvent evt)
|
||||
{
|
||||
bool emitDragFinishedEvent = m_Active;
|
||||
|
||||
m_Active = false;
|
||||
|
||||
if (m_Handle.HasMouseCapture())
|
||||
{
|
||||
m_Handle.ReleaseMouse();
|
||||
}
|
||||
|
||||
evt.StopImmediatePropagation();
|
||||
|
||||
// Recalculate which corner to dock to
|
||||
m_WindowDockingLayout.CalculateDockingCornerAndOffset(target.layout, target.parent.layout);
|
||||
m_WindowDockingLayout.ClampToParentWindow();
|
||||
|
||||
// Use the docking results to figure which of left/right and top/bottom needs to be set.
|
||||
m_WindowDockingLayout.ApplyPosition(target);
|
||||
|
||||
// Signal that the dragging has finished.
|
||||
if (emitDragFinishedEvent && OnDragFinished != null)
|
||||
OnDragFinished();
|
||||
}
|
||||
|
||||
void OnGeometryChanged(GeometryChangedEvent geometryChangedEvent)
|
||||
{
|
||||
// Make the target clamp to the border of the window if the
|
||||
// parent window becomes too small to contain it.
|
||||
if (target.parent.layout.width < target.layout.width)
|
||||
{
|
||||
if (m_WindowDockingLayout.dockingLeft)
|
||||
{
|
||||
target.style.left = 0f;
|
||||
target.style.right = float.NaN;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.style.left = float.NaN;
|
||||
target.style.right = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.parent.layout.height < target.layout.height)
|
||||
{
|
||||
if (m_WindowDockingLayout.dockingTop)
|
||||
{
|
||||
target.style.top = 0f;
|
||||
target.style.bottom = float.NaN;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.style.top = float.NaN;
|
||||
target.style.bottom = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnParentGeometryChanged(GeometryChangedEvent geometryChangedEvent)
|
||||
{
|
||||
// Check if the parent window can no longer contain the target window.
|
||||
// If the window is out of bounds, make one edge clamp to the border of the
|
||||
// parent window.
|
||||
if (target.layout.xMin < 0f)
|
||||
{
|
||||
target.style.left = 0f;
|
||||
target.style.right = float.NaN;
|
||||
}
|
||||
|
||||
if (target.layout.xMax > geometryChangedEvent.newRect.width)
|
||||
{
|
||||
target.style.left = float.NaN;
|
||||
target.style.right = 0f;
|
||||
}
|
||||
|
||||
if (target.layout.yMax > geometryChangedEvent.newRect.height)
|
||||
{
|
||||
target.style.top = float.NaN;
|
||||
target.style.bottom = 0f;
|
||||
}
|
||||
|
||||
if (target.layout.yMin < 0f)
|
||||
{
|
||||
target.style.top = 0f;
|
||||
target.style.bottom = float.NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class MaterialGraphPreviewGenerator : IDisposable
|
||||
{
|
||||
private readonly Scene m_Scene;
|
||||
static Mesh s_Quad;
|
||||
private Camera m_Camera;
|
||||
private Light Light0 { get; set; }
|
||||
private Light Light1 { get; set; }
|
||||
|
||||
private Material m_CheckerboardMaterial;
|
||||
|
||||
private static readonly Mesh[] s_Meshes = { null, null, null, null, null };
|
||||
private static readonly GUIContent[] s_MeshIcons = { null, null, null, null, null };
|
||||
private static readonly GUIContent[] s_LightIcons = { null, null };
|
||||
private static readonly GUIContent[] s_TimeIcons = { null, null };
|
||||
|
||||
protected static GameObject CreateLight()
|
||||
{
|
||||
GameObject lightGO = EditorUtility.CreateGameObjectWithHideFlags("PreRenderLight", HideFlags.HideAndDontSave, typeof(Light));
|
||||
var light = lightGO.GetComponent<Light>();
|
||||
light.type = LightType.Directional;
|
||||
light.intensity = 1.0f;
|
||||
light.enabled = false;
|
||||
return lightGO;
|
||||
}
|
||||
|
||||
public MaterialGraphPreviewGenerator()
|
||||
{
|
||||
m_Scene = EditorSceneManager.NewPreviewScene();
|
||||
var camGO = EditorUtility.CreateGameObjectWithHideFlags("Preview Scene Camera", HideFlags.HideAndDontSave, typeof(Camera));
|
||||
SceneManager.MoveGameObjectToScene(camGO, m_Scene);
|
||||
|
||||
m_Camera = camGO.GetComponent<Camera>();
|
||||
EditorUtility.SetCameraAnimateMaterials(m_Camera, true);
|
||||
|
||||
m_Camera.cameraType = CameraType.Preview;
|
||||
m_Camera.enabled = false;
|
||||
m_Camera.clearFlags = CameraClearFlags.Depth;
|
||||
m_Camera.fieldOfView = 15;
|
||||
m_Camera.farClipPlane = 10.0f;
|
||||
m_Camera.nearClipPlane = 2.0f;
|
||||
m_Camera.backgroundColor = new Color(49.0f / 255.0f, 49.0f / 255.0f, 49.0f / 255.0f, 1.0f);
|
||||
|
||||
// Explicitly use forward rendering for all previews
|
||||
// (deferred fails when generating some static previews at editor launch; and we never want
|
||||
// vertex lit previews if that is chosen in the player settings)
|
||||
m_Camera.renderingPath = RenderingPath.Forward;
|
||||
m_Camera.useOcclusionCulling = false;
|
||||
m_Camera.scene = m_Scene;
|
||||
|
||||
var l0 = CreateLight();
|
||||
SceneManager.MoveGameObjectToScene(l0, m_Scene);
|
||||
|
||||
//previewScene.AddGameObject(l0);
|
||||
Light0 = l0.GetComponent<Light>();
|
||||
|
||||
var l1 = CreateLight();
|
||||
SceneManager.MoveGameObjectToScene(l1, m_Scene);
|
||||
|
||||
//previewScene.AddGameObject(l1);
|
||||
Light1 = l1.GetComponent<Light>();
|
||||
|
||||
Light0.color = new Color(0.769f, 0.769f, 0.769f, 1); // SceneView.kSceneViewFrontLight
|
||||
Light1.transform.rotation = Quaternion.Euler(340, 218, 177);
|
||||
Light1.color = new Color(.4f, .4f, .45f, 0f) * .7f;
|
||||
|
||||
m_CheckerboardMaterial = new Material(Shader.Find("Hidden/Checkerboard"));
|
||||
m_CheckerboardMaterial.shader.hideFlags = HideFlags.HideAndDontSave;
|
||||
m_CheckerboardMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
if (s_Meshes[0] == null)
|
||||
{
|
||||
var handleGo = (GameObject)EditorGUIUtility.LoadRequired("Previews/PreviewMaterials.fbx");
|
||||
|
||||
// @TODO: temp workaround to make it not render in the scene
|
||||
handleGo.SetActive(false);
|
||||
foreach (Transform t in handleGo.transform)
|
||||
{
|
||||
var meshFilter = t.GetComponent<MeshFilter>();
|
||||
switch (t.name)
|
||||
{
|
||||
case "sphere":
|
||||
s_Meshes[0] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "cube":
|
||||
s_Meshes[1] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "cylinder":
|
||||
s_Meshes[2] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "torus":
|
||||
s_Meshes[3] = meshFilter.sharedMesh;
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning("Something is wrong, weird object found: " + t.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s_MeshIcons[0] = EditorGUIUtility.IconContent("PreMatSphere");
|
||||
s_MeshIcons[1] = EditorGUIUtility.IconContent("PreMatCube");
|
||||
s_MeshIcons[2] = EditorGUIUtility.IconContent("PreMatCylinder");
|
||||
s_MeshIcons[3] = EditorGUIUtility.IconContent("PreMatTorus");
|
||||
s_MeshIcons[4] = EditorGUIUtility.IconContent("PreMatQuad");
|
||||
|
||||
s_LightIcons[0] = EditorGUIUtility.IconContent("PreMatLight0");
|
||||
s_LightIcons[1] = EditorGUIUtility.IconContent("PreMatLight1");
|
||||
|
||||
s_TimeIcons[0] = EditorGUIUtility.IconContent("PlayButton");
|
||||
s_TimeIcons[1] = EditorGUIUtility.IconContent("PauseButton");
|
||||
|
||||
Mesh quadMesh = Resources.GetBuiltinResource(typeof(Mesh), "Quad.fbx") as Mesh;
|
||||
s_Meshes[4] = quadMesh;
|
||||
}
|
||||
}
|
||||
|
||||
public static Mesh quad
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Quad != null)
|
||||
return s_Quad;
|
||||
|
||||
var vertices = new[]
|
||||
{
|
||||
new Vector3(-1f, -1f, 0f),
|
||||
new Vector3(1f, 1f, 0f),
|
||||
new Vector3(1f, -1f, 0f),
|
||||
new Vector3(-1f, 1f, 0f)
|
||||
};
|
||||
|
||||
var uvs = new[]
|
||||
{
|
||||
new Vector2(0f, 0f),
|
||||
new Vector2(1f, 1f),
|
||||
new Vector2(1f, 0f),
|
||||
new Vector2(0f, 1f)
|
||||
};
|
||||
|
||||
var indices = new[] { 0, 1, 2, 1, 0, 3 };
|
||||
|
||||
s_Quad = new Mesh
|
||||
{
|
||||
vertices = vertices,
|
||||
uv = uvs,
|
||||
triangles = indices
|
||||
};
|
||||
s_Quad.RecalculateNormals();
|
||||
s_Quad.RecalculateBounds();
|
||||
|
||||
return s_Quad;
|
||||
}
|
||||
}
|
||||
|
||||
public void DoRenderPreview(RenderTexture renderTexture, Material mat, Mesh mesh, PreviewMode mode, bool allowSRP, float time, MaterialPropertyBlock properties = null)
|
||||
{
|
||||
if (mat == null || mat.shader == null)
|
||||
return;
|
||||
|
||||
m_Camera.targetTexture = renderTexture;
|
||||
if (mode == PreviewMode.Preview3D)
|
||||
{
|
||||
m_Camera.transform.position = -Vector3.forward * 5;
|
||||
m_Camera.transform.rotation = Quaternion.identity;
|
||||
m_Camera.orthographic = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Camera.transform.position = -Vector3.forward * 2;
|
||||
m_Camera.transform.rotation = Quaternion.identity;
|
||||
m_Camera.orthographicSize = 1;
|
||||
m_Camera.orthographic = true;
|
||||
}
|
||||
|
||||
m_Camera.targetTexture = renderTexture;
|
||||
var previousRenderTexure = RenderTexture.active;
|
||||
RenderTexture.active = renderTexture;
|
||||
GL.Clear(true, true, Color.black);
|
||||
Graphics.Blit(Texture2D.whiteTexture, renderTexture, m_CheckerboardMaterial);
|
||||
|
||||
EditorUtility.SetCameraAnimateMaterialsTime(m_Camera, time);
|
||||
Light0.enabled = true;
|
||||
Light0.intensity = 1.0f;
|
||||
Light0.transform.rotation = Quaternion.Euler(50f, 50f, 0);
|
||||
Light1.enabled = true;
|
||||
Light1.intensity = 1.0f;
|
||||
m_Camera.clearFlags = CameraClearFlags.Depth;
|
||||
|
||||
Mesh previewMesh = mesh == null ? s_Meshes[0] : mesh;
|
||||
|
||||
Graphics.DrawMesh(
|
||||
mode == PreviewMode.Preview3D ? previewMesh : quad,
|
||||
mode == PreviewMode.Preview3D ? Matrix4x4.TRS(-previewMesh.bounds.center, Quaternion.identity, Vector3.one) : Matrix4x4.identity,
|
||||
mat,
|
||||
1,
|
||||
m_Camera,
|
||||
0,
|
||||
properties,
|
||||
ShadowCastingMode.Off,
|
||||
false,
|
||||
null,
|
||||
false);
|
||||
|
||||
var oldAllowPipes = Unsupported.useScriptableRenderPipeline;
|
||||
Unsupported.useScriptableRenderPipeline = allowSRP;
|
||||
m_Camera.Render();
|
||||
Unsupported.useScriptableRenderPipeline = oldAllowPipes;
|
||||
|
||||
RenderTexture.active = previousRenderTexure;
|
||||
|
||||
Light0.enabled = false;
|
||||
Light1.enabled = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Light0 != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(Light0.gameObject);
|
||||
Light0 = null;
|
||||
}
|
||||
|
||||
if (Light1 != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(Light1.gameObject);
|
||||
Light1 = null;
|
||||
}
|
||||
|
||||
if (m_Camera != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(m_Camera.gameObject);
|
||||
m_Camera = null;
|
||||
}
|
||||
|
||||
if (m_CheckerboardMaterial != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(m_CheckerboardMaterial);
|
||||
m_CheckerboardMaterial = null;
|
||||
}
|
||||
|
||||
EditorSceneManager.ClosePreviewScene(m_Scene);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
enum PreviewRate
|
||||
{
|
||||
Full,
|
||||
Throttled,
|
||||
Off
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.Searcher;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
public class SearchWindowAdapter : SearcherAdapter
|
||||
{
|
||||
readonly VisualTreeAsset m_DefaultItemTemplate;
|
||||
public override bool HasDetailsPanel => false;
|
||||
|
||||
public SearchWindowAdapter(string title) : base(title)
|
||||
{
|
||||
m_DefaultItemTemplate = Resources.Load<VisualTreeAsset>("SearcherItem");
|
||||
}
|
||||
}
|
||||
|
||||
internal class SearchNodeItem : SearcherItem
|
||||
{
|
||||
public NodeEntry NodeGUID;
|
||||
|
||||
public SearchNodeItem(string name, NodeEntry nodeGUID, string[] synonyms,
|
||||
string help = " ", List<SearchNodeItem> children = null) : base(name)
|
||||
{
|
||||
NodeGUID = nodeGUID;
|
||||
Synonyms = synonyms;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,408 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.Searcher;
|
||||
using UnityEngine.Profiling;
|
||||
using UnityEngine.Pool;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
internal struct NodeEntry
|
||||
{
|
||||
public string[] title;
|
||||
public AbstractMaterialNode node;
|
||||
public int compatibleSlotId;
|
||||
public string slotName;
|
||||
}
|
||||
|
||||
class SearchWindowProvider : ScriptableObject
|
||||
{
|
||||
internal EditorWindow m_EditorWindow;
|
||||
internal GraphData m_Graph;
|
||||
internal GraphView m_GraphView;
|
||||
internal Texture2D m_Icon;
|
||||
public List<NodeEntry> currentNodeEntries;
|
||||
public ShaderPort connectedPort { get; set; }
|
||||
public bool nodeNeedsRepositioning { get; set; }
|
||||
public SlotReference targetSlotReference { get; internal set; }
|
||||
public Vector2 targetPosition { get; internal set; }
|
||||
public VisualElement target { get; internal set; }
|
||||
public bool regenerateEntries { get; set; }
|
||||
private const string k_HiddenFolderName = "Hidden";
|
||||
|
||||
public void Initialize(EditorWindow editorWindow, GraphData graph, GraphView graphView)
|
||||
{
|
||||
m_EditorWindow = editorWindow;
|
||||
m_Graph = graph;
|
||||
m_GraphView = graphView;
|
||||
GenerateNodeEntries();
|
||||
|
||||
// Transparent icon to trick search window into indenting items
|
||||
m_Icon = new Texture2D(1, 1);
|
||||
m_Icon.SetPixel(0, 0, new Color(0, 0, 0, 0));
|
||||
m_Icon.Apply();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (m_Icon != null)
|
||||
{
|
||||
DestroyImmediate(m_Icon);
|
||||
m_Icon = null;
|
||||
}
|
||||
}
|
||||
|
||||
List<int> m_Ids;
|
||||
List<MaterialSlot> m_Slots = new List<MaterialSlot>();
|
||||
|
||||
public void GenerateNodeEntries()
|
||||
{
|
||||
Profiler.BeginSample("SearchWindowProvider.GenerateNodeEntries");
|
||||
// First build up temporary data structure containing group & title as an array of strings (the last one is the actual title) and associated node type.
|
||||
List<NodeEntry> nodeEntries = new List<NodeEntry>();
|
||||
|
||||
if (target is ContextView contextView)
|
||||
{
|
||||
// Iterate all BlockFieldDescriptors currently cached on GraphData
|
||||
foreach (var field in m_Graph.blockFieldDescriptors)
|
||||
{
|
||||
if (field.isHidden)
|
||||
continue;
|
||||
|
||||
// Test stage
|
||||
if (field.shaderStage != contextView.contextData.shaderStage)
|
||||
continue;
|
||||
|
||||
// Create title
|
||||
List<string> title = ListPool<string>.Get();
|
||||
if (!string.IsNullOrEmpty(field.path))
|
||||
{
|
||||
var path = field.path.Split('/').ToList();
|
||||
title.AddRange(path);
|
||||
}
|
||||
title.Add(field.displayName);
|
||||
|
||||
// Create and initialize BlockNode instance then add entry
|
||||
var node = (BlockNode)Activator.CreateInstance(typeof(BlockNode));
|
||||
node.Init(field);
|
||||
AddEntries(node, title.ToArray(), nodeEntries);
|
||||
}
|
||||
|
||||
SortEntries(nodeEntries);
|
||||
currentNodeEntries = nodeEntries;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var type in NodeClassCache.knownNodeTypes)
|
||||
{
|
||||
if ((!type.IsClass || type.IsAbstract)
|
||||
|| type == typeof(PropertyNode)
|
||||
|| type == typeof(KeywordNode)
|
||||
|| type == typeof(SubGraphNode))
|
||||
continue;
|
||||
|
||||
TitleAttribute titleAttribute = NodeClassCache.GetAttributeOnNodeType<TitleAttribute>(type);
|
||||
if (titleAttribute != null)
|
||||
{
|
||||
var node = (AbstractMaterialNode)Activator.CreateInstance(type);
|
||||
if (ShaderGraphPreferences.allowDeprecatedBehaviors && node.latestVersion > 0)
|
||||
{
|
||||
var versions = node.allowedNodeVersions ?? Enumerable.Range(0, node.latestVersion + 1);
|
||||
bool multiple = (versions.Count() > 1);
|
||||
foreach (int i in versions)
|
||||
{
|
||||
var depNode = (AbstractMaterialNode)Activator.CreateInstance(type);
|
||||
depNode.ChangeVersion(i);
|
||||
if (multiple)
|
||||
AddEntries(depNode, titleAttribute.title.Append($"V{i}").ToArray(), nodeEntries);
|
||||
else
|
||||
AddEntries(depNode, titleAttribute.title, nodeEntries);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddEntries(node, titleAttribute.title, nodeEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var guid in AssetDatabase.FindAssets(string.Format("t:{0}", typeof(SubGraphAsset))))
|
||||
{
|
||||
var asset = AssetDatabase.LoadAssetAtPath<SubGraphAsset>(AssetDatabase.GUIDToAssetPath(guid));
|
||||
var node = new SubGraphNode { asset = asset };
|
||||
var title = asset.path.Split('/').ToList();
|
||||
|
||||
if (asset.descendents.Contains(m_Graph.assetGuid) || asset.assetGuid == m_Graph.assetGuid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(asset.path))
|
||||
{
|
||||
AddEntries(node, new string[1] { asset.name }, nodeEntries);
|
||||
}
|
||||
else if (title[0] != k_HiddenFolderName)
|
||||
{
|
||||
title.Add(asset.name);
|
||||
AddEntries(node, title.ToArray(), nodeEntries);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var property in m_Graph.properties)
|
||||
{
|
||||
if (property is Serialization.MultiJsonInternal.UnknownShaderPropertyType)
|
||||
continue;
|
||||
|
||||
var node = new PropertyNode();
|
||||
node.property = property;
|
||||
AddEntries(node, new[] { "Properties", "Property: " + property.displayName }, nodeEntries);
|
||||
}
|
||||
foreach (var keyword in m_Graph.keywords)
|
||||
{
|
||||
var node = new KeywordNode();
|
||||
node.keyword = keyword;
|
||||
AddEntries(node, new[] { "Keywords", "Keyword: " + keyword.displayName }, nodeEntries);
|
||||
}
|
||||
|
||||
SortEntries(nodeEntries);
|
||||
currentNodeEntries = nodeEntries;
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
void SortEntries(List<NodeEntry> nodeEntries)
|
||||
{
|
||||
// Sort the entries lexicographically by group then title with the requirement that items always comes before sub-groups in the same group.
|
||||
// Example result:
|
||||
// - Art/BlendMode
|
||||
// - Art/Adjustments/ColorBalance
|
||||
// - Art/Adjustments/Contrast
|
||||
nodeEntries.Sort((entry1, entry2) =>
|
||||
{
|
||||
for (var i = 0; i < entry1.title.Length; i++)
|
||||
{
|
||||
if (i >= entry2.title.Length)
|
||||
return 1;
|
||||
var value = entry1.title[i].CompareTo(entry2.title[i]);
|
||||
if (value != 0)
|
||||
{
|
||||
// Make sure that leaves go before nodes
|
||||
if (entry1.title.Length != entry2.title.Length && (i == entry1.title.Length - 1 || i == entry2.title.Length - 1))
|
||||
{
|
||||
//once nodes are sorted, sort slot entries by slot order instead of alphebetically
|
||||
var alphaOrder = entry1.title.Length < entry2.title.Length ? -1 : 1;
|
||||
var slotOrder = entry1.compatibleSlotId.CompareTo(entry2.compatibleSlotId);
|
||||
return alphaOrder.CompareTo(slotOrder);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void AddEntries(AbstractMaterialNode node, string[] title, List<NodeEntry> addNodeEntries)
|
||||
{
|
||||
if (m_Graph.isSubGraph && !node.allowedInSubGraph)
|
||||
return;
|
||||
if (!m_Graph.isSubGraph && !node.allowedInMainGraph)
|
||||
return;
|
||||
if (connectedPort == null)
|
||||
{
|
||||
addNodeEntries.Add(new NodeEntry
|
||||
{
|
||||
node = node,
|
||||
title = title,
|
||||
compatibleSlotId = -1
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var connectedSlot = connectedPort.slot;
|
||||
m_Slots.Clear();
|
||||
node.GetSlots(m_Slots);
|
||||
var hasSingleSlot = m_Slots.Count(s => s.isOutputSlot != connectedSlot.isOutputSlot) == 1;
|
||||
m_Slots.RemoveAll(slot =>
|
||||
{
|
||||
var materialSlot = (MaterialSlot)slot;
|
||||
return !materialSlot.IsCompatibleWith(connectedSlot);
|
||||
});
|
||||
|
||||
m_Slots.RemoveAll(slot =>
|
||||
{
|
||||
var materialSlot = (MaterialSlot)slot;
|
||||
return !materialSlot.IsCompatibleStageWith(connectedSlot);
|
||||
});
|
||||
|
||||
foreach (var slot in m_Slots)
|
||||
{
|
||||
//var entryTitle = new string[title.Length];
|
||||
//title.CopyTo(entryTitle, 0);
|
||||
//entryTitle[entryTitle.Length - 1] += ": " + slot.displayName;
|
||||
addNodeEntries.Add(new NodeEntry
|
||||
{
|
||||
title = title,
|
||||
node = node,
|
||||
compatibleSlotId = slot.id,
|
||||
slotName = slot.displayName
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
class SearcherProvider : SearchWindowProvider
|
||||
{
|
||||
public Searcher.Searcher LoadSearchWindow()
|
||||
{
|
||||
if (regenerateEntries)
|
||||
{
|
||||
GenerateNodeEntries();
|
||||
regenerateEntries = false;
|
||||
}
|
||||
|
||||
//create empty root for searcher tree
|
||||
var root = new List<SearcherItem>();
|
||||
var dummyEntry = new NodeEntry();
|
||||
|
||||
foreach (var nodeEntry in currentNodeEntries)
|
||||
{
|
||||
SearcherItem item = null;
|
||||
SearcherItem parent = null;
|
||||
for (int i = 0; i < nodeEntry.title.Length; i++)
|
||||
{
|
||||
var pathEntry = nodeEntry.title[i];
|
||||
List<SearcherItem> children = parent != null ? parent.Children : root;
|
||||
item = children.Find(x => x.Name == pathEntry);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
//if we have slot entries and are at a leaf, add the slot name to the entry title
|
||||
if (nodeEntry.compatibleSlotId != -1 && i == nodeEntry.title.Length - 1)
|
||||
item = new SearchNodeItem(pathEntry + ": " + nodeEntry.slotName, nodeEntry, nodeEntry.node.synonyms);
|
||||
//if we don't have slot entries and are at a leaf, add userdata to the entry
|
||||
else if (nodeEntry.compatibleSlotId == -1 && i == nodeEntry.title.Length - 1)
|
||||
item = new SearchNodeItem(pathEntry, nodeEntry, nodeEntry.node.synonyms);
|
||||
//if we aren't a leaf, don't add user data
|
||||
else
|
||||
item = new SearchNodeItem(pathEntry, dummyEntry, null);
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parent.AddChild(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
children.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
parent = item;
|
||||
|
||||
if (parent.Depth == 0 && !root.Contains(parent))
|
||||
root.Add(parent);
|
||||
}
|
||||
}
|
||||
|
||||
var nodeDatabase = SearcherDatabase.Create(root, string.Empty, false);
|
||||
|
||||
return new Searcher.Searcher(nodeDatabase, new SearchWindowAdapter("Create Node"));
|
||||
}
|
||||
|
||||
public bool OnSearcherSelectEntry(SearcherItem entry, Vector2 screenMousePosition)
|
||||
{
|
||||
if (entry == null || (entry as SearchNodeItem).NodeGUID.node == null)
|
||||
return true;
|
||||
|
||||
var nodeEntry = (entry as SearchNodeItem).NodeGUID;
|
||||
|
||||
if (nodeEntry.node is PropertyNode propNode)
|
||||
if (propNode.property is Serialization.MultiJsonInternal.UnknownShaderPropertyType)
|
||||
return true;
|
||||
|
||||
var node = CopyNodeForGraph(nodeEntry.node);
|
||||
|
||||
var windowRoot = m_EditorWindow.rootVisualElement;
|
||||
var windowMousePosition = windowRoot.ChangeCoordinatesTo(windowRoot.parent, screenMousePosition); //- m_EditorWindow.position.position);
|
||||
var graphMousePosition = m_GraphView.contentViewContainer.WorldToLocal(windowMousePosition);
|
||||
|
||||
m_Graph.owner.RegisterCompleteObjectUndo("Add " + node.name);
|
||||
|
||||
if (node is BlockNode blockNode)
|
||||
{
|
||||
if (!(target is ContextView contextView))
|
||||
return true;
|
||||
|
||||
// Test against all current BlockNodes in the Context
|
||||
// Never allow duplicate BlockNodes
|
||||
if (contextView.contextData.blocks.Where(x => x.value.name == blockNode.name).FirstOrDefault().value != null)
|
||||
return true;
|
||||
|
||||
// Insert block to Data
|
||||
blockNode.owner = m_Graph;
|
||||
int index = contextView.GetInsertionIndex(screenMousePosition);
|
||||
m_Graph.AddBlock(blockNode, contextView.contextData, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
var drawState = node.drawState;
|
||||
drawState.position = new Rect(graphMousePosition, Vector2.zero);
|
||||
node.drawState = drawState;
|
||||
m_Graph.AddNode(node);
|
||||
|
||||
if (connectedPort != null)
|
||||
{
|
||||
var connectedSlot = connectedPort.slot;
|
||||
var connectedSlotReference = connectedSlot.owner.GetSlotReference(connectedSlot.id);
|
||||
var compatibleSlotReference = node.GetSlotReference(nodeEntry.compatibleSlotId);
|
||||
|
||||
var fromReference = connectedSlot.isOutputSlot ? connectedSlotReference : compatibleSlotReference;
|
||||
var toReference = connectedSlot.isOutputSlot ? compatibleSlotReference : connectedSlotReference;
|
||||
m_Graph.Connect(fromReference, toReference);
|
||||
|
||||
nodeNeedsRepositioning = true;
|
||||
targetSlotReference = compatibleSlotReference;
|
||||
targetPosition = graphMousePosition;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public AbstractMaterialNode CopyNodeForGraph(AbstractMaterialNode oldNode)
|
||||
{
|
||||
var newNode = (AbstractMaterialNode)Activator.CreateInstance(oldNode.GetType());
|
||||
if (ShaderGraphPreferences.allowDeprecatedBehaviors && oldNode.sgVersion != newNode.sgVersion)
|
||||
{
|
||||
newNode.ChangeVersion(oldNode.sgVersion);
|
||||
}
|
||||
if (newNode is SubGraphNode subgraphNode)
|
||||
{
|
||||
subgraphNode.asset = ((SubGraphNode)oldNode).asset;
|
||||
}
|
||||
else if (newNode is PropertyNode propertyNode)
|
||||
{
|
||||
propertyNode.owner = m_Graph;
|
||||
propertyNode.property = ((PropertyNode)oldNode).property;
|
||||
propertyNode.owner = null;
|
||||
}
|
||||
else if (newNode is KeywordNode keywordNode)
|
||||
{
|
||||
keywordNode.owner = m_Graph;
|
||||
keywordNode.keyword = ((KeywordNode)oldNode).keyword;
|
||||
keywordNode.owner = null;
|
||||
}
|
||||
else if (newNode is BlockNode blockNode)
|
||||
{
|
||||
blockNode.owner = m_Graph;
|
||||
blockNode.Init(((BlockNode)oldNode).descriptor);
|
||||
blockNode.owner = null;
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views.Blackboard
|
||||
{
|
||||
class BlackboardViewModel : SGViewModel
|
||||
{
|
||||
List<Type> m_ShaderInputTypes;
|
||||
|
||||
public override void ConstructFromModel(GraphData graphData)
|
||||
{
|
||||
m_ShaderInputTypes = TypeCache.GetTypesWithAttribute<BlackboardInputInfo>().ToList();
|
||||
}
|
||||
|
||||
public override void WriteToModel()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
abstract class SGViewModel
|
||||
{
|
||||
GraphData m_Model;
|
||||
public GraphData model
|
||||
{
|
||||
get => m_Model;
|
||||
private set
|
||||
{
|
||||
if (model != value)
|
||||
{
|
||||
m_Model = value;
|
||||
ConstructFromModel(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void ConstructFromModel(GraphData graphData);
|
||||
public abstract void WriteToModel();
|
||||
}
|
||||
}
|
@@ -0,0 +1,126 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Serialization;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
sealed class ContextView : StackNode
|
||||
{
|
||||
ContextData m_ContextData;
|
||||
|
||||
// Currently we only need one Port per context
|
||||
// As the Contexts are hardcoded we know their directions
|
||||
Port m_Port;
|
||||
|
||||
//need this from graph view specifically for nodecreation
|
||||
EditorWindow m_EditorWindow;
|
||||
|
||||
// When dealing with more Contexts, `name` should be serialized in the ContextData
|
||||
// Right now we dont do this so we dont overcommit to serializing unknowns
|
||||
public ContextView(string name, ContextData contextData, EditorWindow editorWindow)
|
||||
{
|
||||
// Set data
|
||||
m_ContextData = contextData;
|
||||
m_EditorWindow = editorWindow;
|
||||
|
||||
// Header
|
||||
var headerLabel = new Label() { name = "headerLabel" };
|
||||
headerLabel.text = name;
|
||||
headerContainer.Add(headerLabel);
|
||||
}
|
||||
|
||||
public ContextData contextData => m_ContextData;
|
||||
public Port port => m_Port;
|
||||
|
||||
// We need to use graphViewChange.movedElements to check whether a BlockNode has moved onto the GraphView
|
||||
// but Nodes return in movedElements when they are mid-drag because they are removed from the stack (placeholder)
|
||||
// StackNode has `dragEntered` but its protected so we need `isDragging`
|
||||
public bool isDragging => dragEntered;
|
||||
|
||||
public void AddPort(Direction direction)
|
||||
{
|
||||
var capacity = direction == Direction.Input ? Port.Capacity.Single : Port.Capacity.Multi;
|
||||
var container = direction == Direction.Input ? inputContainer : outputContainer;
|
||||
m_Port = Port.Create<Edge>(Orientation.Vertical, direction, capacity, null);
|
||||
m_Port.portName = "";
|
||||
|
||||
// Vertical ports have no representation in Model
|
||||
// Therefore we need to disable interaction
|
||||
m_Port.pickingMode = PickingMode.Ignore;
|
||||
|
||||
container.Add(m_Port);
|
||||
}
|
||||
|
||||
public void InsertBlock(MaterialNodeView nodeView)
|
||||
{
|
||||
if (!(nodeView.userData is BlockNode blockNode))
|
||||
return;
|
||||
|
||||
// If index is -1 the node is being added to the end of the Stack
|
||||
if (blockNode.index == -1)
|
||||
{
|
||||
AddElement(nodeView);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add or Insert based on index
|
||||
if (blockNode.index >= contentContainer.childCount)
|
||||
{
|
||||
AddElement(nodeView);
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertElement(blockNode.index, nodeView);
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertElements(int insertIndex, IEnumerable<GraphElement> elements)
|
||||
{
|
||||
var blockDatas = elements.Select(x => x.userData as BlockNode).ToArray();
|
||||
for (int i = 0; i < blockDatas.Length; i++)
|
||||
{
|
||||
contextData.blocks.Remove(blockDatas[i]);
|
||||
}
|
||||
|
||||
int count = elements.Count();
|
||||
var refs = new JsonRef<BlockNode>[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
refs[i] = blockDatas[i];
|
||||
}
|
||||
|
||||
contextData.blocks.InsertRange(insertIndex, refs);
|
||||
}
|
||||
|
||||
protected override bool AcceptsElement(GraphElement element, ref int proposedIndex, int maxIndex)
|
||||
{
|
||||
return element.userData is BlockNode blockNode && blockNode.descriptor != null &&
|
||||
blockNode.descriptor.shaderStage == contextData.shaderStage;
|
||||
}
|
||||
|
||||
protected override void OnSeparatorContextualMenuEvent(ContextualMenuPopulateEvent evt, int separatorIndex)
|
||||
{
|
||||
//we need to arbitrarily add the editor position values because node creation context
|
||||
//exptects a non local coordinate
|
||||
Vector2 mousePosition = evt.mousePosition + m_EditorWindow.position.position;
|
||||
base.OnSeparatorContextualMenuEvent(evt, separatorIndex);
|
||||
|
||||
var graphView = GetFirstAncestorOfType<MaterialGraphView>();
|
||||
|
||||
evt.menu.InsertAction(0, "Create Node", (e) =>
|
||||
{
|
||||
var context = new NodeCreationContext()
|
||||
{
|
||||
screenMousePosition = mousePosition,
|
||||
target = this,
|
||||
index = separatorIndex,
|
||||
};
|
||||
graphView.nodeCreationRequest(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using UnityEditor.UIElements;
|
||||
using System.Globalization;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class FloatField : DoubleField
|
||||
{
|
||||
protected override string ValueToString(double v)
|
||||
{
|
||||
return ((float)v).ToString(CultureInfo.InvariantCulture.NumberFormat);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class GradientEdge : Edge
|
||||
{
|
||||
readonly CustomStyleProperty<Color> k_InputColorProperty = new CustomStyleProperty<Color>("--edge-input-color");
|
||||
readonly CustomStyleProperty<Color> k_OutputColorProperty = new CustomStyleProperty<Color>("--edge-output-color");
|
||||
|
||||
Color m_InputColor;
|
||||
Color m_OutputColor;
|
||||
|
||||
public Color inputColor
|
||||
{
|
||||
get { return m_InputColor; }
|
||||
}
|
||||
|
||||
public Color outputColor
|
||||
{
|
||||
get { return m_OutputColor; }
|
||||
}
|
||||
|
||||
public GradientEdge()
|
||||
{
|
||||
m_InputColor = defaultColor;
|
||||
m_OutputColor = defaultColor;
|
||||
|
||||
RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
|
||||
}
|
||||
|
||||
public void UpdateClasses(ConcreteSlotValueType outputType, ConcreteSlotValueType inputType)
|
||||
{
|
||||
ClearClassList();
|
||||
AddToClassList("edge");
|
||||
AddToClassList("from" + outputType);
|
||||
AddToClassList("to" + inputType);
|
||||
}
|
||||
|
||||
private void OnCustomStyleResolved(CustomStyleResolvedEvent e)
|
||||
{
|
||||
Color inputValue;
|
||||
Color outputValue;
|
||||
|
||||
ICustomStyle styles = e.customStyle;
|
||||
if (styles.TryGetValue(k_InputColorProperty, out inputValue))
|
||||
m_InputColor = inputValue;
|
||||
|
||||
if (styles.TryGetValue(k_OutputColorProperty, out outputValue))
|
||||
m_OutputColor = outputValue;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.ShaderGraph.Drawing.Interfaces;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing.Views
|
||||
{
|
||||
class GraphSubWindow : GraphElement, ISGResizable
|
||||
{
|
||||
Dragger m_Dragger;
|
||||
|
||||
// This needs to be something that each subclass defines for itself at creation time
|
||||
// if they all use the same they'll be stacked on top of each other at SG window creation
|
||||
protected WindowDockingLayout windowDockingLayout { get; private set; } = new WindowDockingLayout
|
||||
{
|
||||
dockingTop = true,
|
||||
dockingLeft = false,
|
||||
verticalOffset = 8,
|
||||
horizontalOffset = 8,
|
||||
};
|
||||
|
||||
// Used to cache the window docking layout between resizing operations as it interferes with window resizing operations
|
||||
private IStyle cachedWindowDockingStyle;
|
||||
|
||||
protected VisualElement m_MainContainer;
|
||||
protected VisualElement m_Root;
|
||||
protected Label m_TitleLabel;
|
||||
protected Label m_SubTitleLabel;
|
||||
protected ScrollView m_ScrollView;
|
||||
protected VisualElement m_ContentContainer;
|
||||
protected VisualElement m_HeaderItem;
|
||||
protected GraphView m_GraphView;
|
||||
|
||||
// These are used as default values for styling and layout purposes
|
||||
// They can be overriden if a child class wants to roll its own style and layout behavior
|
||||
public virtual string layoutKey => "UnityEditor.ShaderGraph.SubWindow";
|
||||
public virtual string styleName => "GraphSubWindow";
|
||||
public virtual string UxmlName => "GraphSubWindow";
|
||||
|
||||
// Each sub-window will override these if they need to
|
||||
public virtual string elementName => "";
|
||||
public virtual string windowTitle => "";
|
||||
|
||||
public GraphView graphView
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isWindowed && m_GraphView == null)
|
||||
m_GraphView = GetFirstAncestorOfType<GraphView>();
|
||||
return m_GraphView;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (!isWindowed)
|
||||
return;
|
||||
m_GraphView = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ISelectable> selection => graphView?.selection;
|
||||
|
||||
public override string title
|
||||
{
|
||||
get { return m_TitleLabel.text; }
|
||||
set { m_TitleLabel.text = value; }
|
||||
}
|
||||
|
||||
public string subTitle
|
||||
{
|
||||
get { return m_SubTitleLabel.text; }
|
||||
set { m_SubTitleLabel.text = value; }
|
||||
}
|
||||
|
||||
// Intended for future handling of docking to sides of the shader graph window
|
||||
bool m_IsWindowed;
|
||||
public bool isWindowed
|
||||
{
|
||||
get { return m_IsWindowed; }
|
||||
set
|
||||
{
|
||||
if (m_IsWindowed == value) return;
|
||||
|
||||
if (value)
|
||||
{
|
||||
capabilities &= ~Capabilities.Movable;
|
||||
AddToClassList("windowed");
|
||||
this.RemoveManipulator(m_Dragger);
|
||||
}
|
||||
else
|
||||
{
|
||||
capabilities |= Capabilities.Movable;
|
||||
RemoveFromClassList("windowed");
|
||||
this.AddManipulator(m_Dragger);
|
||||
}
|
||||
m_IsWindowed = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override VisualElement contentContainer => m_ContentContainer;
|
||||
|
||||
private bool m_IsResizable = false;
|
||||
|
||||
// Can be set by child classes as needed
|
||||
protected bool isWindowResizable
|
||||
{
|
||||
get => m_IsResizable;
|
||||
set
|
||||
{
|
||||
if (m_IsResizable != value)
|
||||
{
|
||||
m_IsResizable = value;
|
||||
HandleResizingBehavior(m_IsResizable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleResizingBehavior(bool isResizable)
|
||||
{
|
||||
if (isResizable)
|
||||
{
|
||||
var resizeElement = this.Q<ResizableElement>();
|
||||
resizeElement.BindOnResizeCallback(OnWindowResize);
|
||||
hierarchy.Add(resizeElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
var resizeElement = this.Q<ResizableElement>();
|
||||
resizeElement.SetResizeRules(ResizableElement.Resizer.None);
|
||||
hierarchy.Remove(resizeElement);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetResizingRules(ResizableElement.Resizer resizeDirections)
|
||||
{
|
||||
var resizeElement = this.Q<ResizableElement>();
|
||||
resizeElement.SetResizeRules(resizeDirections);
|
||||
}
|
||||
|
||||
private bool m_IsScrollable = false;
|
||||
|
||||
// Can be set by child classes as needed
|
||||
protected bool isWindowScrollable
|
||||
{
|
||||
get => m_IsScrollable;
|
||||
set
|
||||
{
|
||||
if (m_IsScrollable != value)
|
||||
{
|
||||
m_IsScrollable = value;
|
||||
HandleScrollingBehavior(m_IsScrollable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float scrollableWidth
|
||||
{
|
||||
get { return m_ScrollView.contentContainer.layout.width - m_ScrollView.contentViewport.layout.width; }
|
||||
}
|
||||
|
||||
protected float scrollableHeight
|
||||
{
|
||||
get { return contentContainer.layout.height - m_ScrollView.contentViewport.layout.height; }
|
||||
}
|
||||
|
||||
void HandleScrollingBehavior(bool scrollable)
|
||||
{
|
||||
if (scrollable)
|
||||
{
|
||||
// Remove the sections container from the content item and add it to the scrollview
|
||||
m_ContentContainer.RemoveFromHierarchy();
|
||||
m_ScrollView.Add(m_ContentContainer);
|
||||
AddToClassList("scrollable");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove the sections container from the scrollview and add it to the content item
|
||||
m_ContentContainer.RemoveFromHierarchy();
|
||||
m_Root.Add(m_ContentContainer);
|
||||
|
||||
RemoveFromClassList("scrollable");
|
||||
}
|
||||
}
|
||||
|
||||
protected GraphSubWindow(GraphView associatedGraphView) : base()
|
||||
{
|
||||
m_GraphView = associatedGraphView;
|
||||
m_GraphView.Add(this);
|
||||
|
||||
var styleSheet = Resources.Load<StyleSheet>($"Styles/{styleName}");
|
||||
// Setup VisualElement from Stylesheet and UXML file
|
||||
styleSheets.Add(styleSheet);
|
||||
var uxml = Resources.Load<VisualTreeAsset>($"UXML/{UxmlName}");
|
||||
m_MainContainer = uxml.Instantiate();
|
||||
m_MainContainer.AddToClassList("mainContainer");
|
||||
|
||||
m_Root = m_MainContainer.Q("content");
|
||||
m_HeaderItem = m_MainContainer.Q("header");
|
||||
m_HeaderItem.AddToClassList("subWindowHeader");
|
||||
m_ScrollView = m_MainContainer.Q<ScrollView>("scrollView");
|
||||
m_TitleLabel = m_MainContainer.Q<Label>(name: "titleLabel");
|
||||
m_SubTitleLabel = m_MainContainer.Q<Label>(name: "subTitleLabel");
|
||||
m_ContentContainer = m_MainContainer.Q(name: "contentContainer");
|
||||
|
||||
hierarchy.Add(m_MainContainer);
|
||||
|
||||
capabilities |= Capabilities.Movable | Capabilities.Resizable;
|
||||
style.overflow = Overflow.Hidden;
|
||||
focusable = false;
|
||||
|
||||
name = elementName;
|
||||
title = windowTitle;
|
||||
|
||||
ClearClassList();
|
||||
AddToClassList(name);
|
||||
|
||||
BuildManipulators();
|
||||
|
||||
/* Event interception to prevent GraphView manipulators from being triggered */
|
||||
RegisterCallback<DragUpdatedEvent>(e =>
|
||||
{
|
||||
e.StopPropagation();
|
||||
});
|
||||
|
||||
// prevent Zoomer manipulator
|
||||
RegisterCallback<WheelEvent>(e =>
|
||||
{
|
||||
e.StopPropagation();
|
||||
});
|
||||
|
||||
RegisterCallback<MouseDownEvent>(e =>
|
||||
{
|
||||
// prevent ContentDragger manipulator
|
||||
e.StopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
internal void ShowWindow()
|
||||
{
|
||||
this.style.visibility = Visibility.Visible;
|
||||
this.m_ScrollView.style.display = DisplayStyle.Flex;
|
||||
contentContainer.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
internal void HideWindow()
|
||||
{
|
||||
this.style.visibility = Visibility.Hidden;
|
||||
this.m_ScrollView.style.display = DisplayStyle.None;
|
||||
contentContainer.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void BuildManipulators()
|
||||
{
|
||||
m_Dragger = new Dragger { clampToParentEdges = true };
|
||||
RegisterCallback<MouseUpEvent>(OnMoveEnd);
|
||||
this.AddManipulator(m_Dragger);
|
||||
}
|
||||
|
||||
#region Layout
|
||||
public void ClampToParentLayout(Rect parentLayout)
|
||||
{
|
||||
windowDockingLayout.CalculateDockingCornerAndOffset(layout, parentLayout);
|
||||
windowDockingLayout.ClampToParentWindow();
|
||||
|
||||
// If the parent shader graph window is being resized smaller than this window on either axis
|
||||
if (parentLayout.width < this.layout.width || parentLayout.height < this.layout.height)
|
||||
{
|
||||
// Don't adjust the sub window in this case as it causes flickering errors and looks broken
|
||||
}
|
||||
else
|
||||
{
|
||||
windowDockingLayout.ApplyPosition(this);
|
||||
}
|
||||
|
||||
SerializeLayout();
|
||||
}
|
||||
|
||||
public void OnStartResize()
|
||||
{
|
||||
cachedWindowDockingStyle = this.style;
|
||||
}
|
||||
|
||||
public void OnResized()
|
||||
{
|
||||
this.style.left = cachedWindowDockingStyle.left;
|
||||
this.style.right = cachedWindowDockingStyle.right;
|
||||
this.style.bottom = cachedWindowDockingStyle.bottom;
|
||||
this.style.top = cachedWindowDockingStyle.top;
|
||||
|
||||
windowDockingLayout.size = layout.size;
|
||||
SerializeLayout();
|
||||
}
|
||||
|
||||
public void DeserializeLayout()
|
||||
{
|
||||
var serializedLayout = EditorUserSettings.GetConfigValue(layoutKey);
|
||||
if (!string.IsNullOrEmpty(serializedLayout))
|
||||
windowDockingLayout = JsonUtility.FromJson<WindowDockingLayout>(serializedLayout);
|
||||
else
|
||||
{
|
||||
// The window size needs to come from the stylesheet or UXML as opposed to being defined in code
|
||||
windowDockingLayout.size = layout.size;
|
||||
}
|
||||
|
||||
windowDockingLayout.ApplySize(this);
|
||||
windowDockingLayout.ApplyPosition(this);
|
||||
}
|
||||
|
||||
protected void AddStyleSheetFromPath(string styleSheetPath)
|
||||
{
|
||||
StyleSheet sheetAsset = Resources.Load<StyleSheet>(styleSheetPath);;
|
||||
|
||||
if (sheetAsset == null)
|
||||
{
|
||||
Debug.LogWarning(string.Format("Style sheet not found for path \"{0}\"", styleSheetPath));
|
||||
return;
|
||||
}
|
||||
styleSheets.Add(sheetAsset);
|
||||
}
|
||||
|
||||
void SerializeLayout()
|
||||
{
|
||||
windowDockingLayout.size = layout.size;
|
||||
var serializedLayout = JsonUtility.ToJson(windowDockingLayout);
|
||||
EditorUserSettings.SetConfigValue(layoutKey, serializedLayout);
|
||||
}
|
||||
|
||||
void OnMoveEnd(MouseUpEvent upEvent)
|
||||
{
|
||||
windowDockingLayout.CalculateDockingCornerAndOffset(layout, graphView.layout);
|
||||
windowDockingLayout.ClampToParentWindow();
|
||||
|
||||
SerializeLayout();
|
||||
}
|
||||
|
||||
public bool CanResizePastParentBounds()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnWindowResize(MouseUpEvent upEvent)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
// Similar in function to the old EditorGUILayout.HelpBox
|
||||
class HelpBoxRow : VisualElement
|
||||
{
|
||||
VisualElement m_ContentContainer;
|
||||
VisualElement m_LabelContainer;
|
||||
|
||||
public override VisualElement contentContainer
|
||||
{
|
||||
get { return m_ContentContainer; }
|
||||
}
|
||||
|
||||
public HelpBoxRow(MessageType type)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/HelpBoxRow"));
|
||||
VisualElement container = new VisualElement {name = "container"};
|
||||
m_ContentContainer = new VisualElement { name = "content" };
|
||||
m_LabelContainer = new VisualElement {name = "label" };
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MessageType.None:
|
||||
container.AddToClassList("help-box-row-style-info");
|
||||
break;
|
||||
case MessageType.Info:
|
||||
container.AddToClassList("help-box-row-style-info");
|
||||
break;
|
||||
case MessageType.Warning:
|
||||
container.AddToClassList("help-box-row-style-warning");
|
||||
break;
|
||||
case MessageType.Error:
|
||||
container.AddToClassList("help-box-row-style-error");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
container.Add(m_LabelContainer);
|
||||
container.Add(m_ContentContainer);
|
||||
|
||||
hierarchy.Add(container);
|
||||
}
|
||||
|
||||
public static VisualElement TryGetDeprecatedHelpBoxRow(string deprecatedTypeName, Action upgradeAction)
|
||||
{
|
||||
string depString = $"The {deprecatedTypeName} has new updates. This version maintains the old behavior. " +
|
||||
$"If you update a {deprecatedTypeName}, you can use Undo to change it back. See the {deprecatedTypeName} " +
|
||||
$"documentation for more information.";
|
||||
Button upgradeButton = new Button(upgradeAction) { text = "Update" , tooltip = depString};
|
||||
if (!ShaderGraphPreferences.allowDeprecatedBehaviors)
|
||||
{
|
||||
HelpBoxRow help = new HelpBoxRow(MessageType.Warning);
|
||||
var label = new Label("DEPRECATED: Hover for info")
|
||||
{
|
||||
tooltip = depString
|
||||
};
|
||||
help.Add(label);
|
||||
help.contentContainer.Add(upgradeButton);
|
||||
return help;
|
||||
}
|
||||
else
|
||||
{
|
||||
return upgradeButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEditor.Graphing;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
[Serializable]
|
||||
public enum HlslSourceType { File, String };
|
||||
|
||||
internal class HlslFunctionView : VisualElement
|
||||
{
|
||||
private EnumField m_Type;
|
||||
private TextField m_FunctionName;
|
||||
private ObjectField m_FunctionSource;
|
||||
private TextField m_FunctionBody;
|
||||
|
||||
internal HlslFunctionView(CustomFunctionNode node)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/HlslFunctionView"));
|
||||
Draw(node);
|
||||
}
|
||||
|
||||
private void Draw(CustomFunctionNode node)
|
||||
{
|
||||
var currentControls = this.Children().ToArray();
|
||||
for (int i = 0; i < currentControls.Length; i++)
|
||||
currentControls[i].RemoveFromHierarchy();
|
||||
|
||||
m_Type = new EnumField(node.sourceType);
|
||||
m_Type.RegisterValueChangedCallback(s =>
|
||||
{
|
||||
if ((HlslSourceType)s.newValue != node.sourceType)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Type");
|
||||
node.sourceType = (HlslSourceType)s.newValue;
|
||||
Draw(node);
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
m_FunctionName = new TextField { value = node.functionName, multiline = false };
|
||||
m_FunctionName.RegisterCallback<FocusInEvent>(s =>
|
||||
{
|
||||
if (m_FunctionName.value == CustomFunctionNode.defaultFunctionName)
|
||||
m_FunctionName.value = "";
|
||||
});
|
||||
m_FunctionName.RegisterCallback<FocusOutEvent>(s =>
|
||||
{
|
||||
if (m_FunctionName.value == "")
|
||||
m_FunctionName.value = CustomFunctionNode.defaultFunctionName;
|
||||
else
|
||||
m_FunctionName.value = NodeUtils.ConvertToValidHLSLIdentifier(m_FunctionName.value);
|
||||
|
||||
if (m_FunctionName.value != node.functionName)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Name");
|
||||
node.functionName = m_FunctionName.value;
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
string path = AssetDatabase.GUIDToAssetPath(node.functionSource);
|
||||
m_FunctionSource = new ObjectField() { value = AssetDatabase.LoadAssetAtPath<TextAsset>(path), objectType = typeof(TextAsset)};
|
||||
m_FunctionSource.RegisterValueChangedCallback(s =>
|
||||
{
|
||||
long localId;
|
||||
string guidString = string.Empty;
|
||||
if (s.newValue != null)
|
||||
{
|
||||
AssetDatabase.TryGetGUIDAndLocalFileIdentifier((TextAsset)s.newValue, out guidString, out localId);
|
||||
}
|
||||
|
||||
if (guidString != node.functionSource)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Source");
|
||||
node.functionSource = guidString;
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
m_FunctionBody = new TextField { value = node.functionBody, multiline = true };
|
||||
m_FunctionBody.RegisterCallback<FocusInEvent>(s =>
|
||||
{
|
||||
if (m_FunctionBody.value == CustomFunctionNode.defaultFunctionBody)
|
||||
m_FunctionBody.value = "";
|
||||
});
|
||||
m_FunctionBody.RegisterCallback<FocusOutEvent>(s =>
|
||||
{
|
||||
if (m_FunctionBody.value == "")
|
||||
m_FunctionBody.value = CustomFunctionNode.defaultFunctionBody;
|
||||
|
||||
if (m_FunctionBody.value != node.functionBody)
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo("Change Function Body");
|
||||
node.functionBody = m_FunctionBody.value;
|
||||
node.ValidateNode();
|
||||
node.Dirty(ModificationScope.Graph);
|
||||
}
|
||||
});
|
||||
|
||||
VisualElement typeRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
typeRow.Add(new Label("Type"));
|
||||
typeRow.Add(m_Type);
|
||||
}
|
||||
Add(typeRow);
|
||||
VisualElement nameRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
nameRow.Add(new Label("Name"));
|
||||
nameRow.Add(m_FunctionName);
|
||||
}
|
||||
Add(nameRow);
|
||||
switch (node.sourceType)
|
||||
{
|
||||
case HlslSourceType.File:
|
||||
VisualElement sourceRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
sourceRow.Add(new Label("Source"));
|
||||
sourceRow.Add(m_FunctionSource);
|
||||
}
|
||||
Add(sourceRow);
|
||||
break;
|
||||
case HlslSourceType.String:
|
||||
VisualElement bodyRow = new VisualElement() { name = "Row" };
|
||||
{
|
||||
bodyRow.Add(new Label("Body"));
|
||||
bodyRow.style.height = 200;
|
||||
bodyRow.Add(m_FunctionBody);
|
||||
}
|
||||
Add(bodyRow);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
interface IShaderNodeView : IDisposable
|
||||
{
|
||||
Node gvNode { get; }
|
||||
AbstractMaterialNode node { get; }
|
||||
VisualElement colorElement { get; }
|
||||
void SetColor(Color newColor);
|
||||
void ResetColor();
|
||||
void UpdatePortInputTypes();
|
||||
void OnModified(ModificationScope scope);
|
||||
void AttachMessage(string errString, ShaderCompilerMessageSeverity severity);
|
||||
void ClearMessage();
|
||||
// Searches the ports on this node for one that matches the given slot.
|
||||
// Returns true if found, false if not.
|
||||
bool FindPort(SlotReference slot, out ShaderPort port);
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
/*
|
||||
Field that allows entering a valid HLSL identifier.
|
||||
(variable name, function name, ...) this means
|
||||
no spaces, no funny characters, never starts with a number, ...
|
||||
*/
|
||||
public class IdentifierField : UIElements.TextValueField<string>
|
||||
{
|
||||
IdentifierInput tsInput => (IdentifierInput)textInputBase;
|
||||
|
||||
public new class UxmlFactory : UxmlFactory<IdentifierField, UxmlTraits> {}
|
||||
public new class UxmlTraits : UIElements.TextValueFieldTraits<string, UxmlStringAttributeDescription> {}
|
||||
|
||||
protected override string ValueToString(string v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
protected override string StringToValue(string str)
|
||||
{
|
||||
// Make sure this is a valid hlsl identifier. Allowed characters already ensures the characters are valid
|
||||
// but identifiers can't start with a number so fix this here.
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return "_0";
|
||||
}
|
||||
else if (Char.IsDigit(str[0]))
|
||||
{
|
||||
return "_" + str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public new static readonly string ussClassName = "unity-identifierfield-field";
|
||||
public new static readonly string labelUssClassName = ussClassName + "__label";
|
||||
public new static readonly string inputUssClassName = ussClassName + "__input";
|
||||
|
||||
public IdentifierField() : this((string)null) {}
|
||||
|
||||
public IdentifierField(string label) : base(label, -1, new IdentifierInput())
|
||||
{
|
||||
AddToClassList(ussClassName);
|
||||
labelElement.AddToClassList(labelUssClassName);
|
||||
tsInput.AddToClassList(inputUssClassName);
|
||||
}
|
||||
|
||||
public override void ApplyInputDeviceDelta(Vector3 delta, UIElements.DeltaSpeed speed, string startValue)
|
||||
{
|
||||
tsInput.ApplyInputDeviceDelta(delta, speed, startValue);
|
||||
}
|
||||
|
||||
class IdentifierInput : TextValueInput
|
||||
{
|
||||
IdentifierField parentField => (IdentifierField)parent;
|
||||
|
||||
internal IdentifierInput()
|
||||
{
|
||||
formatString = null;
|
||||
}
|
||||
|
||||
protected override string allowedCharacters
|
||||
{
|
||||
get { return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; }
|
||||
}
|
||||
|
||||
public override void ApplyInputDeviceDelta(Vector3 delta, UIElements.DeltaSpeed speed, string startValue)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string ValueToString(string v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
protected override string StringToValue(string str)
|
||||
{
|
||||
// Make sure this is a valid hlsl identifier. Allowed characters already ensures the characters are valid
|
||||
// but identifiers can't start with a number so fix this here.
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return "_0";
|
||||
}
|
||||
else if (Char.IsDigit(str[0]))
|
||||
{
|
||||
return "_" + str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,726 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Graphing.Util;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.UIElements;
|
||||
using Node = UnityEditor.Experimental.GraphView.Node;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
sealed class MaterialNodeView : Node, IShaderNodeView, IInspectable
|
||||
{
|
||||
PreviewRenderData m_PreviewRenderData;
|
||||
Image m_PreviewImage;
|
||||
// Remove this after updated to the correct API call has landed in trunk. ------------
|
||||
VisualElement m_TitleContainer;
|
||||
new VisualElement m_ButtonContainer;
|
||||
|
||||
VisualElement m_PreviewContainer;
|
||||
VisualElement m_ControlItems;
|
||||
VisualElement m_PreviewFiller;
|
||||
VisualElement m_ControlsDivider;
|
||||
IEdgeConnectorListener m_ConnectorListener;
|
||||
|
||||
MaterialGraphView m_GraphView;
|
||||
|
||||
public string inspectorTitle => $"{node.name} (Node)";
|
||||
public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener, MaterialGraphView graphView)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/MaterialNodeView"));
|
||||
styleSheets.Add(Resources.Load<StyleSheet>($"Styles/ColorMode"));
|
||||
AddToClassList("MaterialNode");
|
||||
|
||||
if (inNode == null)
|
||||
return;
|
||||
|
||||
var contents = this.Q("contents");
|
||||
|
||||
m_GraphView = graphView;
|
||||
mainContainer.style.overflow = StyleKeyword.None; // Override explicit style set in base class
|
||||
m_ConnectorListener = connectorListener;
|
||||
node = inNode;
|
||||
viewDataKey = node.objectId;
|
||||
UpdateTitle();
|
||||
|
||||
// Add disabled overlay
|
||||
Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore });
|
||||
|
||||
// Add controls container
|
||||
var controlsContainer = new VisualElement { name = "controls" };
|
||||
{
|
||||
m_ControlsDivider = new VisualElement { name = "divider" };
|
||||
m_ControlsDivider.AddToClassList("horizontal");
|
||||
controlsContainer.Add(m_ControlsDivider);
|
||||
m_ControlItems = new VisualElement { name = "items" };
|
||||
controlsContainer.Add(m_ControlItems);
|
||||
|
||||
// Instantiate control views from node
|
||||
foreach (var propertyInfo in node.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||
foreach (IControlAttribute attribute in propertyInfo.GetCustomAttributes(typeof(IControlAttribute), false))
|
||||
m_ControlItems.Add(attribute.InstantiateControl(node, propertyInfo));
|
||||
}
|
||||
if (m_ControlItems.childCount > 0)
|
||||
contents.Add(controlsContainer);
|
||||
|
||||
if (node.hasPreview)
|
||||
{
|
||||
// Add actual preview which floats on top of the node
|
||||
m_PreviewContainer = new VisualElement
|
||||
{
|
||||
name = "previewContainer",
|
||||
style = { overflow = Overflow.Hidden },
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
m_PreviewImage = new Image
|
||||
{
|
||||
name = "preview",
|
||||
pickingMode = PickingMode.Ignore,
|
||||
image = Texture2D.whiteTexture,
|
||||
};
|
||||
{
|
||||
// Add preview collapse button on top of preview
|
||||
var collapsePreviewButton = new VisualElement { name = "collapse" };
|
||||
collapsePreviewButton.Add(new VisualElement { name = "icon" });
|
||||
collapsePreviewButton.AddManipulator(new Clickable(() =>
|
||||
{
|
||||
SetPreviewExpandedStateOnSelection(false);
|
||||
}));
|
||||
m_PreviewImage.Add(collapsePreviewButton);
|
||||
}
|
||||
m_PreviewContainer.Add(m_PreviewImage);
|
||||
|
||||
// Hook up preview image to preview manager
|
||||
m_PreviewRenderData = previewManager.GetPreviewRenderData(inNode);
|
||||
m_PreviewRenderData.onPreviewChanged += UpdatePreviewTexture;
|
||||
UpdatePreviewTexture();
|
||||
|
||||
// Add fake preview which pads out the node to provide space for the floating preview
|
||||
m_PreviewFiller = new VisualElement { name = "previewFiller" };
|
||||
m_PreviewFiller.AddToClassList("expanded");
|
||||
{
|
||||
var previewDivider = new VisualElement { name = "divider" };
|
||||
previewDivider.AddToClassList("horizontal");
|
||||
m_PreviewFiller.Add(previewDivider);
|
||||
|
||||
var expandPreviewButton = new VisualElement { name = "expand" };
|
||||
expandPreviewButton.Add(new VisualElement { name = "icon" });
|
||||
expandPreviewButton.AddManipulator(new Clickable(() =>
|
||||
{
|
||||
SetPreviewExpandedStateOnSelection(true);
|
||||
}));
|
||||
m_PreviewFiller.Add(expandPreviewButton);
|
||||
}
|
||||
contents.Add(m_PreviewFiller);
|
||||
|
||||
UpdatePreviewExpandedState(node.previewExpanded);
|
||||
}
|
||||
|
||||
base.expanded = node.drawState.expanded;
|
||||
AddSlots(node.GetSlots<MaterialSlot>());
|
||||
|
||||
if (node is SubGraphNode)
|
||||
{
|
||||
RegisterCallback<MouseDownEvent>(OnSubGraphDoubleClick);
|
||||
}
|
||||
|
||||
m_TitleContainer = this.Q("title");
|
||||
|
||||
if (node is BlockNode blockData)
|
||||
{
|
||||
AddToClassList("blockData");
|
||||
m_TitleContainer.RemoveFromHierarchy();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0));
|
||||
}
|
||||
|
||||
// Update active state
|
||||
SetActive(node.isActive);
|
||||
|
||||
// Register OnMouseHover callbacks for node highlighting
|
||||
RegisterCallback<MouseEnterEvent>(OnMouseHover);
|
||||
RegisterCallback<MouseLeaveEvent>(OnMouseHover);
|
||||
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged += UpdateTitle;
|
||||
}
|
||||
|
||||
public bool FindPort(SlotReference slotRef, out ShaderPort port)
|
||||
{
|
||||
port = inputContainer.Query<ShaderPort>().ToList()
|
||||
.Concat(outputContainer.Query<ShaderPort>().ToList())
|
||||
.First(p => p.slot.slotReference.Equals(slotRef));
|
||||
|
||||
return port != null;
|
||||
}
|
||||
|
||||
public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity)
|
||||
{
|
||||
ClearMessage();
|
||||
IconBadge badge;
|
||||
if (severity == ShaderCompilerMessageSeverity.Error)
|
||||
{
|
||||
badge = IconBadge.CreateError(errString);
|
||||
}
|
||||
else
|
||||
{
|
||||
badge = IconBadge.CreateComment(errString);
|
||||
}
|
||||
|
||||
Add(badge);
|
||||
badge.AttachTo(m_TitleContainer, SpriteAlignment.RightCenter);
|
||||
}
|
||||
|
||||
public void SetActive(bool state)
|
||||
{
|
||||
// Setup
|
||||
var disabledString = "disabled";
|
||||
var portDisabledString = "inactive";
|
||||
|
||||
|
||||
if (!state)
|
||||
{
|
||||
// Add elements to disabled class list
|
||||
AddToClassList(disabledString);
|
||||
|
||||
var inputPorts = inputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in inputPorts)
|
||||
{
|
||||
port.AddToClassList(portDisabledString);
|
||||
}
|
||||
var outputPorts = outputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in outputPorts)
|
||||
{
|
||||
port.AddToClassList(portDisabledString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove elements from disabled class list
|
||||
RemoveFromClassList(disabledString);
|
||||
|
||||
var inputPorts = inputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in inputPorts)
|
||||
{
|
||||
port.RemoveFromClassList(portDisabledString);
|
||||
}
|
||||
var outputPorts = outputContainer.Query<ShaderPort>().ToList();
|
||||
foreach (var port in outputPorts)
|
||||
{
|
||||
port.RemoveFromClassList(portDisabledString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearMessage()
|
||||
{
|
||||
var badge = this.Q<IconBadge>();
|
||||
badge?.Detach();
|
||||
badge?.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
public VisualElement colorElement
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
static readonly StyleColor noColor = new StyleColor(StyleKeyword.Null);
|
||||
public void SetColor(Color color)
|
||||
{
|
||||
m_TitleContainer.style.borderBottomColor = color;
|
||||
}
|
||||
|
||||
public void ResetColor()
|
||||
{
|
||||
m_TitleContainer.style.borderBottomColor = noColor;
|
||||
}
|
||||
|
||||
public Color GetColor()
|
||||
{
|
||||
return m_TitleContainer.resolvedStyle.borderBottomColor;
|
||||
}
|
||||
|
||||
void OnSubGraphDoubleClick(MouseDownEvent evt)
|
||||
{
|
||||
if (evt.clickCount == 2 && evt.button == 0)
|
||||
{
|
||||
SubGraphNode subgraphNode = node as SubGraphNode;
|
||||
|
||||
var path = AssetDatabase.GUIDToAssetPath(subgraphNode.subGraphGuid);
|
||||
ShaderGraphImporterEditor.ShowGraphEditWindow(path);
|
||||
}
|
||||
}
|
||||
|
||||
public Node gvNode => this;
|
||||
|
||||
[Inspectable("Node", null)]
|
||||
public AbstractMaterialNode node { get; private set; }
|
||||
|
||||
public override bool expanded
|
||||
{
|
||||
get => base.expanded;
|
||||
set
|
||||
{
|
||||
if (base.expanded == value)
|
||||
return;
|
||||
|
||||
base.expanded = value;
|
||||
|
||||
if (node.drawState.expanded != value)
|
||||
{
|
||||
var ds = node.drawState;
|
||||
ds.expanded = value;
|
||||
node.drawState = ds;
|
||||
}
|
||||
|
||||
foreach (var inputPort in inputContainer.Query<ShaderPort>().ToList())
|
||||
{
|
||||
inputPort.parent.style.visibility = inputPort.style.visibility;
|
||||
}
|
||||
|
||||
RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed
|
||||
}
|
||||
}
|
||||
|
||||
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
if (evt.target is Node)
|
||||
{
|
||||
var canViewShader = node.hasPreview || node is SubGraphOutputNode;
|
||||
evt.menu.AppendAction("Copy Shader", CopyToClipboard,
|
||||
_ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
|
||||
GenerationMode.ForReals);
|
||||
evt.menu.AppendAction("Show Generated Code", ShowGeneratedCode,
|
||||
_ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
|
||||
GenerationMode.ForReals);
|
||||
|
||||
if (Unsupported.IsDeveloperMode())
|
||||
{
|
||||
evt.menu.AppendAction("Show Preview Code", ShowGeneratedCode,
|
||||
_ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden,
|
||||
GenerationMode.Preview);
|
||||
}
|
||||
}
|
||||
|
||||
base.BuildContextualMenu(evt);
|
||||
}
|
||||
|
||||
void CopyToClipboard(DropdownMenuAction action)
|
||||
{
|
||||
GUIUtility.systemCopyBuffer = ConvertToShader((GenerationMode)action.userData);
|
||||
}
|
||||
|
||||
public string SanitizeName(string name)
|
||||
{
|
||||
return new string(name.Where(c => !Char.IsWhiteSpace(c)).ToArray());
|
||||
}
|
||||
|
||||
public void ShowGeneratedCode(DropdownMenuAction action)
|
||||
{
|
||||
string name = GetFirstAncestorOfType<GraphEditorView>().assetName;
|
||||
var mode = (GenerationMode)action.userData;
|
||||
|
||||
string path = String.Format("Temp/GeneratedFromGraph-{0}-{1}-{2}{3}.shader", SanitizeName(name),
|
||||
SanitizeName(node.name), node.objectId, mode == GenerationMode.Preview ? "-Preview" : "");
|
||||
if (GraphUtil.WriteToFile(path, ConvertToShader(mode)))
|
||||
GraphUtil.OpenFile(path);
|
||||
}
|
||||
|
||||
string ConvertToShader(GenerationMode mode)
|
||||
{
|
||||
var generator = new Generator(node.owner, node, mode, node.name, null);
|
||||
return generator.generatedShader;
|
||||
}
|
||||
|
||||
void SetNodesAsDirty()
|
||||
{
|
||||
var editorView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
var nodeList = m_GraphView.Query<MaterialNodeView>().ToList();
|
||||
editorView.colorManager.SetNodesDirty(nodeList);
|
||||
}
|
||||
|
||||
void UpdateNodeViews()
|
||||
{
|
||||
var editorView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
var nodeList = m_GraphView.Query<MaterialNodeView>().ToList();
|
||||
editorView.colorManager.UpdateNodeViews(nodeList);
|
||||
}
|
||||
|
||||
public object GetObjectToInspect()
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate)
|
||||
{
|
||||
if (propertyDrawer is IGetNodePropertyDrawerPropertyData nodePropertyDrawer)
|
||||
{
|
||||
nodePropertyDrawer.GetPropertyData(SetNodesAsDirty, UpdateNodeViews);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSelfSelected()
|
||||
{
|
||||
m_GraphView.ClearSelection();
|
||||
m_GraphView.AddToSelection(this);
|
||||
}
|
||||
|
||||
protected override void ToggleCollapse()
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo(!expanded ? "Expand Nodes" : "Collapse Nodes");
|
||||
expanded = !expanded;
|
||||
|
||||
// If selected, expand/collapse the other applicable nodes that are also selected
|
||||
if (selected)
|
||||
{
|
||||
m_GraphView.SetNodeExpandedForSelectedNodes(expanded, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SetPreviewExpandedStateOnSelection(bool state)
|
||||
{
|
||||
// If selected, expand/collapse the other applicable nodes that are also selected
|
||||
if (selected)
|
||||
{
|
||||
m_GraphView.SetPreviewExpandedForSelectedNodes(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.owner.owner.RegisterCompleteObjectUndo(state ? "Expand Previews" : "Collapse Previews");
|
||||
node.previewExpanded = state;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanToggleNodeExpanded()
|
||||
{
|
||||
return !(node is BlockNode) && m_CollapseButton.enabledInHierarchy;
|
||||
}
|
||||
|
||||
void UpdatePreviewExpandedState(bool expanded)
|
||||
{
|
||||
node.previewExpanded = expanded;
|
||||
if (m_PreviewFiller == null)
|
||||
return;
|
||||
if (expanded)
|
||||
{
|
||||
if (m_PreviewContainer.parent != this)
|
||||
{
|
||||
Add(m_PreviewContainer);
|
||||
m_PreviewContainer.PlaceBehind(this.Q("selection-border"));
|
||||
}
|
||||
m_PreviewFiller.AddToClassList("expanded");
|
||||
m_PreviewFiller.RemoveFromClassList("collapsed");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_PreviewContainer.parent == m_PreviewFiller)
|
||||
{
|
||||
m_PreviewContainer.RemoveFromHierarchy();
|
||||
}
|
||||
m_PreviewFiller.RemoveFromClassList("expanded");
|
||||
m_PreviewFiller.AddToClassList("collapsed");
|
||||
}
|
||||
UpdatePreviewTexture();
|
||||
}
|
||||
|
||||
void UpdateTitle()
|
||||
{
|
||||
if (node is SubGraphNode subGraphNode && subGraphNode.asset != null)
|
||||
title = subGraphNode.asset.name;
|
||||
else
|
||||
{
|
||||
if (node.sgVersion < node.latestVersion)
|
||||
{
|
||||
if (ShaderGraphPreferences.allowDeprecatedBehaviors)
|
||||
{
|
||||
title = node.name + $" (Deprecated V{node.sgVersion})";
|
||||
}
|
||||
else
|
||||
{
|
||||
title = node.name + $" (Deprecated)";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
title = node.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateShaderPortsForSlots(bool inputSlots, List<MaterialSlot> allSlots, ShaderPort[] slotShaderPorts)
|
||||
{
|
||||
VisualElement portContainer = inputSlots ? inputContainer : outputContainer;
|
||||
var existingPorts = portContainer.Query<ShaderPort>().ToList();
|
||||
foreach (ShaderPort shaderPort in existingPorts)
|
||||
{
|
||||
var currentSlotId = shaderPort.slot.id;
|
||||
int newSlotIndex = allSlots.FindIndex(s => s.id == currentSlotId);
|
||||
if (newSlotIndex < 0)
|
||||
{
|
||||
// slot doesn't exist anymore, remove it
|
||||
if (inputSlots)
|
||||
portContainer.Remove(shaderPort.parent); // remove parent (includes the InputView)
|
||||
else
|
||||
portContainer.Remove(shaderPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newSlot = allSlots[newSlotIndex];
|
||||
slotShaderPorts[newSlotIndex] = shaderPort;
|
||||
|
||||
// these should probably be in an UpdateShaderPort(shaderPort, newSlot) function
|
||||
shaderPort.slot = newSlot;
|
||||
shaderPort.portName = newSlot.displayName;
|
||||
|
||||
if (inputSlots) // input slots also have to update the InputView
|
||||
UpdatePortInputView(shaderPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnModified(ModificationScope scope)
|
||||
{
|
||||
UpdateTitle();
|
||||
SetActive(node.isActive);
|
||||
if (node.hasPreview)
|
||||
UpdatePreviewExpandedState(node.previewExpanded);
|
||||
|
||||
base.expanded = node.drawState.expanded;
|
||||
|
||||
switch (scope)
|
||||
{
|
||||
// Update slots to match node modification
|
||||
case ModificationScope.Topological:
|
||||
{
|
||||
var slots = node.GetSlots<MaterialSlot>().ToList();
|
||||
// going to record the corresponding ShaderPort to each slot, so we can order them later
|
||||
ShaderPort[] slotShaderPorts = new ShaderPort[slots.Count];
|
||||
|
||||
// update existing input and output ports
|
||||
UpdateShaderPortsForSlots(true, slots, slotShaderPorts);
|
||||
UpdateShaderPortsForSlots(false, slots, slotShaderPorts);
|
||||
|
||||
// check if there are any new slots that must create new ports
|
||||
for (int i = 0; i < slots.Count; i++)
|
||||
{
|
||||
if (slotShaderPorts[i] == null)
|
||||
slotShaderPorts[i] = AddShaderPortForSlot(slots[i]);
|
||||
}
|
||||
|
||||
// make sure they are in the right order
|
||||
// by bringing each port to front in declaration order
|
||||
// note that this sorts input and output containers at the same time
|
||||
foreach (var shaderPort in slotShaderPorts)
|
||||
{
|
||||
if (shaderPort != null)
|
||||
{
|
||||
if (shaderPort.slot.isInputSlot)
|
||||
shaderPort.parent.BringToFront();
|
||||
else
|
||||
shaderPort.BringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed
|
||||
|
||||
foreach (var listener in m_ControlItems.Children().OfType<AbstractMaterialNodeModificationListener>())
|
||||
{
|
||||
if (listener != null)
|
||||
listener.OnNodeModified(scope);
|
||||
}
|
||||
}
|
||||
|
||||
ShaderPort AddShaderPortForSlot(MaterialSlot slot)
|
||||
{
|
||||
if (slot.hidden)
|
||||
return null;
|
||||
|
||||
ShaderPort port = ShaderPort.Create(slot, m_ConnectorListener);
|
||||
if (slot.isOutputSlot)
|
||||
{
|
||||
outputContainer.Add(port);
|
||||
}
|
||||
else
|
||||
{
|
||||
var portContainer = new VisualElement();
|
||||
portContainer.style.flexDirection = FlexDirection.Row;
|
||||
var portInputView = new PortInputView(slot) { style = { position = Position.Absolute } };
|
||||
portContainer.Add(portInputView);
|
||||
portContainer.Add(port);
|
||||
inputContainer.Add(portContainer);
|
||||
|
||||
// Update active state
|
||||
if (node.isActive)
|
||||
{
|
||||
portInputView.RemoveFromClassList("disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
portInputView.AddToClassList("disabled");
|
||||
}
|
||||
}
|
||||
port.OnDisconnect = OnEdgeDisconnected;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
void AddSlots(IEnumerable<MaterialSlot> slots)
|
||||
{
|
||||
foreach (var slot in slots)
|
||||
AddShaderPortForSlot(slot);
|
||||
// Make sure the visuals are properly updated to reflect port list
|
||||
RefreshPorts();
|
||||
}
|
||||
|
||||
void OnEdgeDisconnected(Port obj)
|
||||
{
|
||||
RefreshExpandedState();
|
||||
}
|
||||
|
||||
static bool GetPortInputView(ShaderPort port, out PortInputView view)
|
||||
{
|
||||
view = port.parent.Q<PortInputView>();
|
||||
return view != null;
|
||||
}
|
||||
|
||||
public void UpdatePortInputTypes()
|
||||
{
|
||||
var portList = inputContainer.Query<ShaderPort>().ToList();
|
||||
portList.AddRange(outputContainer.Query<ShaderPort>().ToList());
|
||||
foreach (var anchor in portList)
|
||||
{
|
||||
var slot = anchor.slot;
|
||||
anchor.portName = slot.displayName;
|
||||
anchor.visualClass = slot.concreteValueType.ToClassName();
|
||||
|
||||
if (GetPortInputView(anchor, out var portInputView))
|
||||
{
|
||||
portInputView.UpdateSlotType();
|
||||
UpdatePortInputVisibility(portInputView, anchor);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var control in m_ControlItems.Children())
|
||||
{
|
||||
if (control is AbstractMaterialNodeModificationListener listener)
|
||||
listener.OnNodeModified(ModificationScope.Graph);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePortInputView(ShaderPort port)
|
||||
{
|
||||
if (GetPortInputView(port, out var portInputView))
|
||||
{
|
||||
portInputView.UpdateSlot(port.slot);
|
||||
UpdatePortInputVisibility(portInputView, port);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePortInputVisibility(PortInputView portInputView, ShaderPort port)
|
||||
{
|
||||
SetElementVisible(portInputView, !port.slot.isConnected);
|
||||
port.parent.style.visibility = port.style.visibility;
|
||||
portInputView.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void SetElementVisible(VisualElement element, bool isVisible)
|
||||
{
|
||||
const string k_HiddenClassList = "hidden";
|
||||
|
||||
if (isVisible)
|
||||
{
|
||||
// Restore default value for visibility by setting it to StyleKeyword.Null.
|
||||
// Setting it to Visibility.Visible would make it visible even if parent is hidden.
|
||||
element.style.visibility = StyleKeyword.Null;
|
||||
element.RemoveFromClassList(k_HiddenClassList);
|
||||
}
|
||||
else
|
||||
{
|
||||
element.style.visibility = Visibility.Hidden;
|
||||
element.AddToClassList(k_HiddenClassList);
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseHover(EventBase evt)
|
||||
{
|
||||
var graphEditorView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
if (graphEditorView == null)
|
||||
return;
|
||||
|
||||
var blackboardProvider = graphEditorView.blackboardProvider;
|
||||
if (blackboardProvider == null)
|
||||
return;
|
||||
|
||||
// Keyword nodes should be highlighted when Blackboard entry is hovered
|
||||
// TODO: Move to new NodeView type when keyword node has unique style
|
||||
if (node is KeywordNode keywordNode)
|
||||
{
|
||||
var keywordRow = blackboardProvider.GetBlackboardRow(keywordNode.keyword);
|
||||
if (keywordRow != null)
|
||||
{
|
||||
if (evt.eventTypeId == MouseEnterEvent.TypeId())
|
||||
{
|
||||
keywordRow.AddToClassList("hovered");
|
||||
}
|
||||
else
|
||||
{
|
||||
keywordRow.RemoveFromClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePreviewTexture()
|
||||
{
|
||||
if (m_PreviewRenderData.texture == null || !node.previewExpanded)
|
||||
{
|
||||
m_PreviewImage.visible = false;
|
||||
m_PreviewImage.image = Texture2D.blackTexture;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PreviewImage.visible = true;
|
||||
m_PreviewImage.AddToClassList("visible");
|
||||
m_PreviewImage.RemoveFromClassList("hidden");
|
||||
if (m_PreviewImage.image != m_PreviewRenderData.texture)
|
||||
m_PreviewImage.image = m_PreviewRenderData.texture;
|
||||
else
|
||||
m_PreviewImage.MarkDirtyRepaint();
|
||||
|
||||
if (m_PreviewRenderData.shaderData.isOutOfDate)
|
||||
m_PreviewImage.tintColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
|
||||
else
|
||||
m_PreviewImage.tintColor = Color.white;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var portInputView in inputContainer.Query<PortInputView>().ToList())
|
||||
portInputView.Dispose();
|
||||
|
||||
node = null;
|
||||
userData = null;
|
||||
if (m_PreviewRenderData != null)
|
||||
{
|
||||
m_PreviewRenderData.onPreviewChanged -= UpdatePreviewTexture;
|
||||
m_PreviewRenderData = null;
|
||||
}
|
||||
ShaderGraphPreferences.onAllowDeprecatedChanged -= UpdateTitle;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class NodeSettingsView : VisualElement
|
||||
{
|
||||
VisualElement m_ContentContainer;
|
||||
|
||||
public NodeSettingsView()
|
||||
{
|
||||
pickingMode = PickingMode.Ignore;
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/NodeSettings"));
|
||||
var uxml = Resources.Load<VisualTreeAsset>("UXML/NodeSettings");
|
||||
uxml.CloneTree(this);
|
||||
// Get the element we want to use as content container
|
||||
m_ContentContainer = this.Q("contentContainer");
|
||||
RegisterCallback<MouseDownEvent>(OnMouseDown);
|
||||
RegisterCallback<MouseUpEvent>(OnMouseUp);
|
||||
}
|
||||
|
||||
void OnMouseUp(MouseUpEvent evt)
|
||||
{
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
void OnMouseDown(MouseDownEvent evt)
|
||||
{
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
public override VisualElement contentContainer
|
||||
{
|
||||
get { return m_ContentContainer; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class PortInputView : GraphElement, IDisposable
|
||||
{
|
||||
readonly CustomStyleProperty<Color> k_EdgeColorProperty = new CustomStyleProperty<Color>("--edge-color");
|
||||
|
||||
Color m_EdgeColor = Color.red;
|
||||
|
||||
public Color edgeColor
|
||||
{
|
||||
get { return m_EdgeColor; }
|
||||
}
|
||||
|
||||
public MaterialSlot slot
|
||||
{
|
||||
get { return m_Slot; }
|
||||
}
|
||||
|
||||
MaterialSlot m_Slot;
|
||||
ConcreteSlotValueType m_SlotType;
|
||||
VisualElement m_Control;
|
||||
VisualElement m_Container;
|
||||
EdgeControl m_EdgeControl;
|
||||
|
||||
public PortInputView(MaterialSlot slot)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/PortInputView"));
|
||||
pickingMode = PickingMode.Ignore;
|
||||
ClearClassList();
|
||||
m_Slot = slot;
|
||||
m_SlotType = slot.concreteValueType;
|
||||
AddToClassList("type" + m_SlotType);
|
||||
|
||||
m_EdgeControl = new EdgeControl
|
||||
{
|
||||
@from = new Vector2(232f - 21f, 11.5f),
|
||||
to = new Vector2(232f, 11.5f),
|
||||
edgeWidth = 2,
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
Add(m_EdgeControl);
|
||||
|
||||
m_Container = new VisualElement { name = "container" };
|
||||
{
|
||||
CreateControl();
|
||||
|
||||
var slotElement = new VisualElement { name = "slot" };
|
||||
{
|
||||
slotElement.Add(new VisualElement { name = "dot" });
|
||||
}
|
||||
m_Container.Add(slotElement);
|
||||
}
|
||||
Add(m_Container);
|
||||
|
||||
m_Container.Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore });
|
||||
RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
|
||||
}
|
||||
|
||||
void OnCustomStyleResolved(CustomStyleResolvedEvent e)
|
||||
{
|
||||
Color colorValue;
|
||||
|
||||
if (e.customStyle.TryGetValue(k_EdgeColorProperty, out colorValue))
|
||||
m_EdgeColor = colorValue;
|
||||
|
||||
m_EdgeControl.UpdateLayout();
|
||||
m_EdgeControl.inputColor = edgeColor;
|
||||
m_EdgeControl.outputColor = edgeColor;
|
||||
}
|
||||
|
||||
public void UpdateSlot(MaterialSlot newSlot)
|
||||
{
|
||||
m_Slot = newSlot;
|
||||
Recreate();
|
||||
}
|
||||
|
||||
public void UpdateSlotType()
|
||||
{
|
||||
if (slot.concreteValueType != m_SlotType)
|
||||
Recreate();
|
||||
}
|
||||
|
||||
void Recreate()
|
||||
{
|
||||
RemoveFromClassList("type" + m_SlotType);
|
||||
m_SlotType = slot.concreteValueType;
|
||||
AddToClassList("type" + m_SlotType);
|
||||
if (m_Control != null)
|
||||
{
|
||||
if (m_Control is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
m_Container.Remove(m_Control);
|
||||
}
|
||||
CreateControl();
|
||||
}
|
||||
|
||||
void CreateControl()
|
||||
{
|
||||
m_Control = slot.InstantiateControl();
|
||||
if (m_Control != null)
|
||||
{
|
||||
m_Container.Insert(0, m_Control);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some slot types don't support an input control, so hide this
|
||||
m_Container.visible = m_EdgeControl.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Control is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class PreviewSceneResources : IDisposable
|
||||
{
|
||||
readonly Scene m_Scene;
|
||||
Camera m_Camera;
|
||||
public Light light0 { get; private set; }
|
||||
public Light light1 { get; private set; }
|
||||
|
||||
Material m_CheckerboardMaterial;
|
||||
Material m_BlitNoAlphaMaterial;
|
||||
|
||||
static readonly Mesh[] s_Meshes = { null, null, null, null, null };
|
||||
static readonly GUIContent[] s_MeshIcons = { null, null, null, null, null };
|
||||
static readonly GUIContent[] s_LightIcons = { null, null };
|
||||
static readonly GUIContent[] s_TimeIcons = { null, null };
|
||||
|
||||
static GameObject CreateLight()
|
||||
{
|
||||
GameObject lightGO = EditorUtility.CreateGameObjectWithHideFlags("PreRenderLight", HideFlags.HideAndDontSave, typeof(Light));
|
||||
var light = lightGO.GetComponent<Light>();
|
||||
light.type = LightType.Directional;
|
||||
light.intensity = 1.0f;
|
||||
light.enabled = false;
|
||||
return lightGO;
|
||||
}
|
||||
|
||||
public PreviewSceneResources()
|
||||
{
|
||||
m_Scene = EditorSceneManager.NewPreviewScene();
|
||||
var camGO = EditorUtility.CreateGameObjectWithHideFlags("Preview Scene Camera", HideFlags.HideAndDontSave, typeof(Camera));
|
||||
SceneManager.MoveGameObjectToScene(camGO, m_Scene);
|
||||
|
||||
m_Camera = camGO.GetComponent<Camera>();
|
||||
EditorUtility.SetCameraAnimateMaterials(camera, true);
|
||||
|
||||
camera.cameraType = CameraType.Preview;
|
||||
camera.enabled = false;
|
||||
camera.clearFlags = CameraClearFlags.Depth;
|
||||
camera.fieldOfView = 15;
|
||||
camera.farClipPlane = 10.0f;
|
||||
camera.nearClipPlane = 2.0f;
|
||||
camera.backgroundColor = new Color(49.0f / 255.0f, 49.0f / 255.0f, 49.0f / 255.0f, 1.0f);
|
||||
|
||||
// Explicitly use forward rendering for all previews
|
||||
// (deferred fails when generating some static previews at editor launch; and we never want
|
||||
// vertex lit previews if that is chosen in the player settings)
|
||||
camera.renderingPath = RenderingPath.Forward;
|
||||
camera.useOcclusionCulling = false;
|
||||
camera.scene = m_Scene;
|
||||
|
||||
var l0 = CreateLight();
|
||||
SceneManager.MoveGameObjectToScene(l0, m_Scene);
|
||||
|
||||
//previewScene.AddGameObject(l0);
|
||||
light0 = l0.GetComponent<Light>();
|
||||
|
||||
var l1 = CreateLight();
|
||||
SceneManager.MoveGameObjectToScene(l1, m_Scene);
|
||||
|
||||
//previewScene.AddGameObject(l1);
|
||||
light1 = l1.GetComponent<Light>();
|
||||
|
||||
light0.color = new Color(0.769f, 0.769f, 0.769f, 1); // SceneView.kSceneViewFrontLight
|
||||
light1.transform.rotation = Quaternion.Euler(340, 218, 177);
|
||||
light1.color = new Color(.4f, .4f, .45f, 0f) * .7f;
|
||||
|
||||
m_CheckerboardMaterial = new Material(Shader.Find("Hidden/Checkerboard"));
|
||||
m_BlitNoAlphaMaterial = new Material(Shader.Find("Hidden/BlitNoAlpha"));
|
||||
checkerboardMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
blitNoAlphaMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
if (s_Meshes[0] == null)
|
||||
{
|
||||
var handleGo = (GameObject)EditorGUIUtility.LoadRequired("Previews/PreviewMaterials.fbx");
|
||||
|
||||
// @TODO: temp workaround to make it not render in the scene
|
||||
handleGo.SetActive(false);
|
||||
foreach (Transform t in handleGo.transform)
|
||||
{
|
||||
var meshFilter = t.GetComponent<MeshFilter>();
|
||||
switch (t.name)
|
||||
{
|
||||
case "sphere":
|
||||
s_Meshes[0] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "cube":
|
||||
s_Meshes[1] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "cylinder":
|
||||
s_Meshes[2] = meshFilter.sharedMesh;
|
||||
break;
|
||||
case "torus":
|
||||
s_Meshes[3] = meshFilter.sharedMesh;
|
||||
break;
|
||||
default:
|
||||
Debug.LogWarning("Something is wrong, weird object found: " + t.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s_MeshIcons[0] = EditorGUIUtility.IconContent("PreMatSphere");
|
||||
s_MeshIcons[1] = EditorGUIUtility.IconContent("PreMatCube");
|
||||
s_MeshIcons[2] = EditorGUIUtility.IconContent("PreMatCylinder");
|
||||
s_MeshIcons[3] = EditorGUIUtility.IconContent("PreMatTorus");
|
||||
s_MeshIcons[4] = EditorGUIUtility.IconContent("PreMatQuad");
|
||||
|
||||
s_LightIcons[0] = EditorGUIUtility.IconContent("PreMatLight0");
|
||||
s_LightIcons[1] = EditorGUIUtility.IconContent("PreMatLight1");
|
||||
|
||||
s_TimeIcons[0] = EditorGUIUtility.IconContent("PlayButton");
|
||||
s_TimeIcons[1] = EditorGUIUtility.IconContent("PauseButton");
|
||||
|
||||
Mesh quadMesh = Resources.GetBuiltinResource(typeof(Mesh), "Quad.fbx") as Mesh;
|
||||
s_Meshes[4] = quadMesh;
|
||||
}
|
||||
}
|
||||
|
||||
public Mesh sphere
|
||||
{
|
||||
get { return s_Meshes[0]; }
|
||||
}
|
||||
|
||||
public Mesh quad
|
||||
{
|
||||
get { return s_Meshes[4]; }
|
||||
}
|
||||
|
||||
public Material checkerboardMaterial
|
||||
{
|
||||
get { return m_CheckerboardMaterial; }
|
||||
}
|
||||
|
||||
public Material blitNoAlphaMaterial
|
||||
{
|
||||
get { return m_BlitNoAlphaMaterial; }
|
||||
}
|
||||
|
||||
public Camera camera
|
||||
{
|
||||
get { return m_Camera; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (light0 != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(light0.gameObject);
|
||||
light0 = null;
|
||||
}
|
||||
|
||||
if (light1 != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(light1.gameObject);
|
||||
light1 = null;
|
||||
}
|
||||
|
||||
if (camera != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(camera.gameObject);
|
||||
m_Camera = null;
|
||||
}
|
||||
|
||||
if (checkerboardMaterial != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(checkerboardMaterial, true);
|
||||
m_CheckerboardMaterial = null;
|
||||
}
|
||||
if (blitNoAlphaMaterial != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(blitNoAlphaMaterial, true);
|
||||
m_BlitNoAlphaMaterial = null;
|
||||
}
|
||||
|
||||
EditorSceneManager.ClosePreviewScene(m_Scene);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,343 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.Graphing;
|
||||
using UnityEditor.Rendering;
|
||||
using UnityEditor.ShaderGraph.Drawing;
|
||||
using UnityEditor.ShaderGraph.Drawing.Controls;
|
||||
using UnityEditor.ShaderGraph.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers;
|
||||
using UnityEditor.ShaderGraph.Drawing.Views.Blackboard;
|
||||
using ContextualMenuManipulator = UnityEngine.UIElements.ContextualMenuManipulator;
|
||||
|
||||
namespace UnityEditor.ShaderGraph
|
||||
{
|
||||
sealed class PropertyNodeView : TokenNode, IShaderNodeView, IInspectable
|
||||
{
|
||||
static readonly Texture2D exposedIcon = Resources.Load<Texture2D>("GraphView/Nodes/BlackboardFieldExposed");
|
||||
|
||||
internal delegate void ChangeDisplayNameCallback(string newDisplayName);
|
||||
|
||||
// When the properties are changed, this delegate is used to trigger an update in the view that represents those properties
|
||||
Action m_propertyViewUpdateTrigger;
|
||||
|
||||
Action m_ResetReferenceNameAction;
|
||||
|
||||
public PropertyNodeView(PropertyNode node, EdgeConnectorListener edgeConnectorListener)
|
||||
: base(null, ShaderPort.Create(node.GetOutputSlots<MaterialSlot>().First(), edgeConnectorListener))
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyNodeView"));
|
||||
this.node = node;
|
||||
viewDataKey = node.objectId.ToString();
|
||||
userData = node;
|
||||
|
||||
// Getting the generatePropertyBlock property to see if it is exposed or not
|
||||
UpdateIcon();
|
||||
|
||||
// Setting the position of the node, otherwise it ends up in the center of the canvas
|
||||
SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0));
|
||||
|
||||
// Removing the title label since it is not used and taking up space
|
||||
this.Q("title-label").RemoveFromHierarchy();
|
||||
|
||||
// Add disabled overlay
|
||||
Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore });
|
||||
|
||||
// Update active state
|
||||
SetActive(node.isActive);
|
||||
|
||||
// Registering the hovering callbacks for highlighting
|
||||
RegisterCallback<MouseEnterEvent>(OnMouseHover);
|
||||
RegisterCallback<MouseLeaveEvent>(OnMouseHover);
|
||||
|
||||
// add the right click context menu
|
||||
IManipulator contextMenuManipulator = new ContextualMenuManipulator(AddContextMenuOptions);
|
||||
this.AddManipulator(contextMenuManipulator);
|
||||
|
||||
// Set callback association for display name updates
|
||||
property.displayNameUpdateTrigger += node.UpdateNodeDisplayName;
|
||||
}
|
||||
|
||||
public Node gvNode => this;
|
||||
public AbstractMaterialNode node { get; }
|
||||
public VisualElement colorElement => null;
|
||||
public string inspectorTitle => $"{property.displayName} (Node)";
|
||||
|
||||
[Inspectable("ShaderInput", null)]
|
||||
AbstractShaderProperty property => (node as PropertyNode)?.property;
|
||||
|
||||
public object GetObjectToInspect()
|
||||
{
|
||||
return property;
|
||||
}
|
||||
|
||||
public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate)
|
||||
{
|
||||
if (propertyDrawer is ShaderInputPropertyDrawer shaderInputPropertyDrawer)
|
||||
{
|
||||
var propNode = node as PropertyNode;
|
||||
var graph = node.owner as GraphData;
|
||||
|
||||
shaderInputPropertyDrawer.GetPropertyData(
|
||||
graph.isSubGraph,
|
||||
graph,
|
||||
this.ChangeExposedField,
|
||||
() => graph.ValidateGraph(),
|
||||
() => graph.OnKeywordChanged(),
|
||||
this.ChangePropertyValue,
|
||||
this.RegisterPropertyChangeUndo,
|
||||
this.MarkNodesAsDirty);
|
||||
|
||||
this.m_propertyViewUpdateTrigger = inspectorUpdateDelegate;
|
||||
this.m_ResetReferenceNameAction = shaderInputPropertyDrawer.ResetReferenceName;
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeExposedField(bool newValue)
|
||||
{
|
||||
property.generatePropertyBlock = newValue;
|
||||
UpdateIcon();
|
||||
}
|
||||
|
||||
void AddContextMenuOptions(ContextualMenuPopulateEvent evt)
|
||||
{
|
||||
// Checks if the reference name has been overridden and appends menu action to reset it, if so
|
||||
if (property.isRenamable &&
|
||||
!string.IsNullOrEmpty(property.overrideReferenceName))
|
||||
{
|
||||
evt.menu.AppendAction(
|
||||
"Reset Reference",
|
||||
e =>
|
||||
{
|
||||
m_ResetReferenceNameAction();
|
||||
DirtyNodes(ModificationScope.Graph);
|
||||
},
|
||||
DropdownMenuAction.AlwaysEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterPropertyChangeUndo(string actionName)
|
||||
{
|
||||
var graph = node.owner as GraphData;
|
||||
graph.owner.RegisterCompleteObjectUndo(actionName);
|
||||
}
|
||||
|
||||
void MarkNodesAsDirty(bool triggerPropertyViewUpdate = false, ModificationScope modificationScope = ModificationScope.Node)
|
||||
{
|
||||
DirtyNodes(modificationScope);
|
||||
if (triggerPropertyViewUpdate)
|
||||
this.m_propertyViewUpdateTrigger();
|
||||
}
|
||||
|
||||
void ChangePropertyValue(object newValue)
|
||||
{
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
switch (property)
|
||||
{
|
||||
case BooleanShaderProperty booleanProperty:
|
||||
booleanProperty.value = ((ToggleData)newValue).isOn;
|
||||
break;
|
||||
case Vector1ShaderProperty vector1Property:
|
||||
vector1Property.value = (float)newValue;
|
||||
break;
|
||||
case Vector2ShaderProperty vector2Property:
|
||||
vector2Property.value = (Vector2)newValue;
|
||||
break;
|
||||
case Vector3ShaderProperty vector3Property:
|
||||
vector3Property.value = (Vector3)newValue;
|
||||
break;
|
||||
case Vector4ShaderProperty vector4Property:
|
||||
vector4Property.value = (Vector4)newValue;
|
||||
break;
|
||||
case ColorShaderProperty colorProperty:
|
||||
colorProperty.value = (Color)newValue;
|
||||
break;
|
||||
case Texture2DShaderProperty texture2DProperty:
|
||||
texture2DProperty.value.texture = (Texture)newValue;
|
||||
break;
|
||||
case Texture2DArrayShaderProperty texture2DArrayProperty:
|
||||
texture2DArrayProperty.value.textureArray = (Texture2DArray)newValue;
|
||||
break;
|
||||
case Texture3DShaderProperty texture3DProperty:
|
||||
texture3DProperty.value.texture = (Texture3D)newValue;
|
||||
break;
|
||||
case CubemapShaderProperty cubemapProperty:
|
||||
cubemapProperty.value.cubemap = (Cubemap)newValue;
|
||||
break;
|
||||
case Matrix2ShaderProperty matrix2Property:
|
||||
matrix2Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case Matrix3ShaderProperty matrix3Property:
|
||||
matrix3Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case Matrix4ShaderProperty matrix4Property:
|
||||
matrix4Property.value = (Matrix4x4)newValue;
|
||||
break;
|
||||
case SamplerStateShaderProperty samplerStateProperty:
|
||||
samplerStateProperty.value = (TextureSamplerState)newValue;
|
||||
break;
|
||||
case GradientShaderProperty gradientProperty:
|
||||
gradientProperty.value = (Gradient)newValue;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
this.MarkDirtyRepaint();
|
||||
}
|
||||
|
||||
void DirtyNodes(ModificationScope modificationScope = ModificationScope.Node)
|
||||
{
|
||||
var graph = node.owner as GraphData;
|
||||
|
||||
var colorManager = GetFirstAncestorOfType<GraphEditorView>().colorManager;
|
||||
var nodes = GetFirstAncestorOfType<GraphEditorView>().graphView.Query<MaterialNodeView>().ToList();
|
||||
|
||||
colorManager.SetNodesDirty(nodes);
|
||||
colorManager.UpdateNodeViews(nodes);
|
||||
|
||||
foreach (var node in graph.GetNodes<PropertyNode>())
|
||||
{
|
||||
node.Dirty(modificationScope);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetColor(Color newColor)
|
||||
{
|
||||
// Nothing to do here yet
|
||||
}
|
||||
|
||||
public void ResetColor()
|
||||
{
|
||||
// Nothing to do here yet
|
||||
}
|
||||
|
||||
public void UpdatePortInputTypes()
|
||||
{
|
||||
}
|
||||
|
||||
public bool FindPort(SlotReference slot, out ShaderPort port)
|
||||
{
|
||||
port = output as ShaderPort;
|
||||
return port != null && port.slot.slotReference.Equals(slot);
|
||||
}
|
||||
|
||||
void UpdateIcon()
|
||||
{
|
||||
var graph = node?.owner as GraphData;
|
||||
if ((graph != null) && (property != null))
|
||||
icon = (graph.isSubGraph || property.isExposed) ? BlackboardProvider.exposedIcon : null;
|
||||
else
|
||||
icon = null;
|
||||
}
|
||||
|
||||
public void OnModified(ModificationScope scope)
|
||||
{
|
||||
//disconnected property nodes are always active
|
||||
if (!node.IsSlotConnected(PropertyNode.OutputSlotId))
|
||||
node.SetActive(true);
|
||||
|
||||
SetActive(node.isActive);
|
||||
|
||||
if (scope == ModificationScope.Graph)
|
||||
{
|
||||
UpdateIcon();
|
||||
}
|
||||
|
||||
if (scope == ModificationScope.Topological || scope == ModificationScope.Node)
|
||||
{
|
||||
// Updating the text label of the output slot
|
||||
var slot = node.GetSlots<MaterialSlot>().ToList().First();
|
||||
this.Q<Label>("type").text = slot.displayName;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActive(bool state)
|
||||
{
|
||||
// Setup
|
||||
var disabledString = "disabled";
|
||||
|
||||
if (!state)
|
||||
{
|
||||
// Add elements to disabled class list
|
||||
AddToClassList(disabledString);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove elements from disabled class list
|
||||
RemoveFromClassList(disabledString);
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity)
|
||||
{
|
||||
ClearMessage();
|
||||
IconBadge badge;
|
||||
if (severity == ShaderCompilerMessageSeverity.Error)
|
||||
{
|
||||
badge = IconBadge.CreateError(errString);
|
||||
}
|
||||
else
|
||||
{
|
||||
badge = IconBadge.CreateComment(errString);
|
||||
}
|
||||
|
||||
Add(badge);
|
||||
badge.AttachTo(this, SpriteAlignment.RightCenter);
|
||||
}
|
||||
|
||||
public void ClearMessage()
|
||||
{
|
||||
var badge = this.Q<IconBadge>();
|
||||
if (badge != null)
|
||||
{
|
||||
badge.Detach();
|
||||
badge.RemoveFromHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
BlackboardRow GetAssociatedBlackboardRow()
|
||||
{
|
||||
var graphView = GetFirstAncestorOfType<GraphEditorView>();
|
||||
if (graphView == null)
|
||||
return null;
|
||||
|
||||
var blackboardProvider = graphView.blackboardProvider;
|
||||
if (blackboardProvider == null)
|
||||
return null;
|
||||
|
||||
var propNode = (PropertyNode)node;
|
||||
return blackboardProvider.GetBlackboardRow(propNode.property);
|
||||
}
|
||||
|
||||
void OnMouseHover(EventBase evt)
|
||||
{
|
||||
var propRow = GetAssociatedBlackboardRow();
|
||||
if (propRow != null)
|
||||
{
|
||||
if (evt.eventTypeId == MouseEnterEvent.TypeId())
|
||||
{
|
||||
propRow.AddToClassList("hovered");
|
||||
}
|
||||
else
|
||||
{
|
||||
propRow.RemoveFromClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
var propRow = GetAssociatedBlackboardRow();
|
||||
// The associated blackboard row can be deleted in which case this property node view is also cleaned up with it, so we want to check for null
|
||||
if (propRow != null)
|
||||
{
|
||||
propRow.RemoveFromClassList("hovered");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UnityEditor.ShaderGraph.Drawing
|
||||
{
|
||||
class PropertyRow : VisualElement
|
||||
{
|
||||
VisualElement m_ContentContainer;
|
||||
VisualElement m_LabelContainer;
|
||||
|
||||
public override VisualElement contentContainer
|
||||
{
|
||||
get { return m_ContentContainer; }
|
||||
}
|
||||
|
||||
public VisualElement label
|
||||
{
|
||||
get { return (m_LabelContainer.childCount > 0) ? m_LabelContainer[0] : null; }
|
||||
set
|
||||
{
|
||||
if (m_LabelContainer.childCount > 0)
|
||||
{
|
||||
m_LabelContainer.Clear();
|
||||
}
|
||||
m_LabelContainer.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyRow(VisualElement label = null)
|
||||
{
|
||||
styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow"));
|
||||
VisualElement container = new VisualElement {name = "container"};
|
||||
m_ContentContainer = new VisualElement { name = "content" };
|
||||
m_LabelContainer = new VisualElement {name = "label" };
|
||||
m_LabelContainer.Add(label);
|
||||
|
||||
container.Add(m_LabelContainer);
|
||||
container.Add(m_ContentContainer);
|
||||
|
||||
hierarchy.Add(container);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user