Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Collections.Generic;
- using System.Linq;
- using UnityEditor;
- using UnityEditor.Callbacks;
- using UnityEditor.Experimental.GraphView;
- using UnityEditor.UIElements;
- using UnityEngine;
- using UnityEngine.UIElements;
- using Object = UnityEngine.Object;
- namespace ModernDialogues.Editor
- {
- /// <summary>
- /// Custom editor window to display the dialogue graph
- /// </summary>
- public class DialogueGraph : EditorWindow
- {
- //private static DialogueGraph instance;
- //public static DialogueGraph Instance { get { return GetWindow<DialogueGraph>(); } }
- private bool isFocused;
- //private InspectorView inspectorView; // Shows details about the selected node
- private const string ConversationName = "New Narrative"; // The name of the dialogue conversation
- private ToolbarMenu dialogueMenu; // Dropdown of available dialogues
- private List<Dialogue> dialogues; // Holds a list of all available dialogues
- //[SerializeField]
- private DialogueGraphView graphView;
- // Reference to the dialogue graph view
- public DialogueGraphView GraphView { get => graphView; set => graphView = value; }
- // Check used to see if we want to load the dialogue after it's created
- public bool LoadAsset { get; set; }
- // Auto save dialogue
- public bool AutoSaveDialogue { get; private set; }
- // Reference to the filename of the current dialogue
- public string Filename { get; set; }
- /// <summary>
- /// Open the dialogue graph
- /// </summary>
- [MenuItem("Graph/Dialogue Graph")]
- public static void OpenDialogueGraphWindow()
- {
- DialogueGraph window = GetWindow<DialogueGraph>();
- window.titleContent = new GUIContent("Dialogue Graph");
- }
- /// <summary>
- /// Open the dialogue graph and load the selected dialogue
- /// </summary>
- /// <param name="instanceId">The ID of the dialogue object</param>
- /// <returns>If we handled the opening of the asset</returns>
- [OnOpenAsset(1)]
- public static bool OnOpenAsset(int instanceId)
- {
- // Chek if the object is a DialogueDatatable and assign it to the datatable variable
- if (EditorUtility.InstanceIDToObject(instanceId) is not Dialogue dialogue)
- return false;
- // Get the filename of the datatable
- //string filename = Path.GetFileNameWithoutExtension(AssetDatabase.GetAssetPath(datatable));
- string filename = AssetDatabase.GetAssetPath(dialogue);
- // Open dialogue graph and load the datatable
- DialogueGraph window = GetWindow<DialogueGraph>();
- window.LoadDialogue(filename);
- window.Filename = filename;
- window.EnableGraph();
- // Set the dropdown field to the opened datatable
- if (window.GraphView.Dialogue != null)
- window.rootVisualElement.Query<ToolbarMenu>().Where(x => x.name == "dialogueMenu").First()
- .text = window.GraphView.Dialogue.ConversationName;
- return true;
- }
- /// <summary>
- /// Setup the dialogue graph window
- /// </summary>
- private void OnEnable()
- {
- //instance = this;
- GenerateToolbar();
- ConstructGraphView();
- GenerateMiniMap();
- }
- /// <summary>
- /// Creates a clickable minimap of the dialogue nodes
- /// </summary>
- private void GenerateMiniMap()
- {
- MiniMap miniMap = new() {anchored = true};
- miniMap.SetPosition(new Rect(position.width - 210, 30, 200, 140));
- GraphView.Add(miniMap);
- }
- /// <summary>
- /// By removing the base element in the window, it removes all children too
- /// </summary>
- private void OnDisable() => rootVisualElement.RemoveAt(0);
- /// <summary>
- /// Constructs the dialogue graph
- /// </summary>
- private void ConstructGraphView()
- {
- GraphView = new DialogueGraphView(this)
- {
- name = "Dialogue Graph"
- };
- TwoPaneSplitView sv = new()
- {
- name = "splitView",
- fixedPaneInitialDimension = 400,
- orientation = TwoPaneSplitViewOrientation.Horizontal
- };
- VisualElement leftPanel = new();
- VisualElement rightPanel = new();
- rightPanel.Add(GraphView);
- leftPanel.style.maxWidth = 400;
- leftPanel.style.minWidth = 400;
- // Displays the name of the conversation on the graph
- Label lblConversationName = new(ConversationName)
- {
- name = "conversationName",
- style =
- {
- fontSize = 36,
- unityFontStyleAndWeight = new StyleEnum<FontStyle>(FontStyle.Bold),
- marginLeft = 5,
- marginTop = 5,
- color = new StyleColor(new Color(1, 1, 1, .25f))
- }
- };
- rightPanel.Add(lblConversationName);
- sv.Add(leftPanel);
- sv.Add(rightPanel);
- GraphView.StretchToParentSize();
- rootVisualElement.Add(sv);
- sv.visible = false;
- }
- /// <summary>
- /// Generates the top toolbar for the graph allowing for basic operations such as switching, loading, and
- /// saving of dialogues
- /// </summary>
- private void GenerateToolbar()
- {
- Toolbar toolbar = new()
- {
- style =
- {
- alignItems = new StyleEnum<Align>(Align.Center)
- }
- };
- dialogueMenu = new ToolbarMenu
- {
- name = "dialogueMenu",
- text = "Select Dialogue",
- style =
- {
- width = 200,
- paddingLeft = 10,
- paddingRight = 10
- }
- };
- toolbar.Add(dialogueMenu);
- GetDialogues();
- // Creates a new datatable
- ToolbarButton newDialogue = new(CreateDialogue)
- {
- name = "newDialogue",
- text = "New Dialogue",
- style =
- {
- paddingLeft = 10,
- paddingRight = 10
- }
- };
- toolbar.Add(newDialogue);
- ToolbarButton duplicateDialogue = new(DuplicateDialogue)
- {
- name = "duplicateDialogue",
- text = "Duplicate Dialogue",
- style =
- {
- paddingLeft = 10,
- paddingRight = 10
- }
- };
- duplicateDialogue.SetEnabled(false);
- toolbar.Add(duplicateDialogue);
- // Deletes the current datatable
- ToolbarButton removeDialogue = new(RemoveDialogue)
- {
- name = "removeDialogue",
- text = "Remove Dialogue",
- style =
- {
- paddingLeft = 10,
- paddingRight = 10
- }
- };
- removeDialogue.SetEnabled(false);
- toolbar.Add(removeDialogue);
- VisualElement saveContainer = new()
- {
- style =
- {
- flexDirection = new StyleEnum<FlexDirection>(FlexDirection.Row),
- position = new StyleEnum<Position>(Position.Absolute),
- right = 0,
- top = 0,
- bottom = 0,
- alignItems = new StyleEnum<Align>(Align.Center)
- }
- };
- // Saves the current datatable
- ToolbarButton saveDialogue = new(() => RequestDataOperation(true))
- {
- name = "saveData",
- text = "Save Dialogue",
- style =
- {
- paddingLeft = 10,
- paddingRight = 10
- }
- };
- saveDialogue.SetEnabled(false);
- saveContainer.Add(saveDialogue);
- ToolbarToggle autoSave = new()
- {
- name = "autoSave",
- text = " Auto Save",
- style =
- {
- paddingLeft = 10,
- paddingRight = 10
- }
- };
- autoSave.RegisterValueChangedCallback(evt => { AutoSaveDialogue = evt.newValue; });
- saveContainer.Add(autoSave);
- toolbar.Add(saveContainer);
- rootVisualElement.Add(toolbar);
- }
- /// <summary>
- /// Get all objects in the project of the specified type
- /// </summary>
- /// <typeparam name="T">The type of object we want to load</typeparam>
- /// <returns>List of objects in the projects of the specified type</returns>
- private IEnumerable<T> GetAllDialogues<T>() where T : Object
- {
- string[] guids = AssetDatabase.FindAssets($"t:{typeof(T)}");
- foreach (string t in guids)
- {
- string assetPath = AssetDatabase.GUIDToAssetPath(t);
- T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
- if (asset != null)
- yield return asset;
- }
- }
- /// <summary>
- /// Save and load the current graph
- /// </summary>
- /// <param name="save">Do we want to save</param>
- private void RequestDataOperation(bool save)
- {
- if (string.IsNullOrEmpty(Filename))
- {
- EditorUtility.DisplayDialog("Invalid file name!", "Please enter a valid file name.", "OK");
- return;
- }
- if (save)
- {
- SaveChanges();
- GraphView.DialogueNeedsSaving = false;
- }
- else
- {
- LoadDialogue(Filename);
- EnableGraph();
- }
- }
- /// <summary>
- /// Creates a new dialogue datatable
- /// </summary>
- private void CreateDialogue()
- {
- Dialogue dialogue = CreateInstance<Dialogue>();
- Utilities.TryGetActiveFolderPath(out string path);
- string assetPath = AssetDatabase.GenerateUniqueAssetPath(path + "/" + ConversationName + ".asset");
- AssetDatabase.CreateAsset(dialogue, assetPath);
- AssetDatabase.SaveAssets();
- RefreshDialogueList();
- dialogue.ConversationName = dialogue.name;
- Filename = assetPath;
- LoadDialogue(Filename);
- EnableGraph();
- dialogueMenu.text = dialogue.ConversationName;
- }
- /// <summary>
- /// Duplicates the current dialogue
- /// </summary>
- private void DuplicateDialogue()
- {
- foreach (string asset in AssetDatabase.FindAssets("t:Dialogue"))
- {
- string path = AssetDatabase.GUIDToAssetPath(asset);
- if (!path.Contains(GraphView.Dialogue.ConversationName + ".asset"))
- continue;
- Utilities.TryGetActiveFolderPath(out string outPath);
- string newPath =
- AssetDatabase.GenerateUniqueAssetPath(outPath + "/" + GraphView.Dialogue.ConversationName +
- ".asset");
- AssetDatabase.CopyAsset(path, newPath);
- AssetDatabase.SaveAssets();
- AssetDatabase.Refresh();
- RefreshDialogueList();
- Filename = newPath;
- LoadAsset = true;
- }
- }
- /// <summary>
- /// Deletes the current dialogue datatable
- /// </summary>
- private void RemoveDialogue()
- {
- foreach (var asset in AssetDatabase.FindAssets("t:Dialogue"))
- {
- var path = AssetDatabase.GUIDToAssetPath(asset);
- if (path.Contains(GraphView.Dialogue.ConversationName + ".asset"))
- {
- AssetDatabase.DeleteAsset(path);
- }
- }
- }
- /// <summary>
- /// Enable the graph basic operations of the graphview window
- /// </summary>
- private void EnableGraph()
- {
- rootVisualElement.Q<Button>("saveData").SetEnabled(true);
- rootVisualElement.Q<Button>("duplicateDialogue").SetEnabled(true);
- rootVisualElement.Q<Button>("removeDialogue").SetEnabled(true);
- rootVisualElement.Q<TwoPaneSplitView>("splitView").visible = true;
- }
- /// <summary>
- /// Disable the graph basic operations of the graphview window
- /// </summary>
- private void DisableGraph()
- {
- rootVisualElement.Q<Button>("saveData").SetEnabled(false);
- rootVisualElement.Q<Button>("duplicateDialogue").SetEnabled(false);
- rootVisualElement.Q<Button>("removeDialogue").SetEnabled(false);
- rootVisualElement.Q<TwoPaneSplitView>("splitView").visible = false;
- }
- private void OnFocus() => isFocused = true;
- /// <summary>
- /// When we change the name of the conversation in the default inspector, we want to reflect the
- /// changes in the graph
- /// </summary>
- private void OnInspectorUpdate()
- {
- if (!isFocused)
- return;
- Selection.activeObject = GraphView.Dialogue;
- if (GraphView.Dialogue != null)
- ((Label) rootVisualElement.Query<Label>().Where(x => x.name == "conversationName")).text =
- GraphView.Dialogue.ConversationName;
- }
- /// <summary>
- /// Adjust the correct position of the minimap whenever the window is resized
- /// </summary>
- private void OnGUI()
- {
- if (Event.current.rawType != EventType.Layout)
- return;
- MiniMap miniMap = GraphView.contentContainer.Q<MiniMap>();
- miniMap.SetPosition(new Rect(GraphView.resolvedStyle.width - 210, 30, 200, 140));
- }
- /// <summary>
- /// We created, deleted, or renamed a dialogue so we need to refresh the dropdown dialogue list
- /// </summary>
- public void RefreshDialogueList()
- {
- GetDialogues();
- if (GraphView.Dialogue != null)
- return;
- DisableGraph();
- dialogueMenu.text = "Select Dialogue";
- }
- /// <summary>
- /// Gets all the dialogue datatables in the entire project
- /// </summary>
- private void GetDialogues()
- {
- dialogueMenu.menu.MenuItems().Clear();
- dialogues = GetAllDialogues<Dialogue>().ToList();
- foreach (Dialogue dialogue in dialogues)
- {
- dialogueMenu.menu.AppendAction(dialogue.ConversationName, _ =>
- {
- if (AutoSaveDialogue)
- {
- SaveChanges();
- }
- else
- {
- if (GraphView.DialogueNeedsSaving)
- SaveChangesPopup.Show(this);
- }
- dialogueMenu.text = dialogue.ConversationName;
- Dialogue newDialogue =
- dialogues.FirstOrDefault(dt => dt.ConversationName == dialogue.ConversationName);
- if (newDialogue == null)
- return;
- //filename = newDatatable.name;
- Filename = AssetDatabase.GetAssetPath(newDialogue);
- LoadDialogue(Filename);
- EnableGraph();
- });
- }
- }
- /// <summary>
- /// If auto save is on, save when the graph window loses focus
- /// </summary>
- private void OnLostFocus()
- {
- if (AutoSaveDialogue)
- RequestDataOperation(true);
- isFocused = false;
- }
- /// <summary>
- /// If auto save is on, save when the graph window closes. If auto save is off,
- /// prompt to save changes.
- /// </summary>
- private void OnDestroy()
- {
- if (AutoSaveDialogue)
- RequestDataOperation(true);
- else if (GraphView.DialogueNeedsSaving)
- SaveChangesPopup.Show(this);
- }
- /// <summary>
- /// Save changes to the dialogue
- /// </summary>
- public override void SaveChanges() => GraphSaveUtility.GetInstance(GraphView).SaveGraph(Filename);
- /// <summary>
- /// Load a dialogue
- /// </summary>
- /// <param name="graphToLoad">The name of the dialogue to load</param>
- public void LoadDialogue(string graphToLoad) =>
- GraphSaveUtility.GetInstance(GraphView).LoadDialogue(graphToLoad);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement