268 lines
8.9 KiB
C#
268 lines
8.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using UnityEditor.Actions;
|
|
using UnityEditor.EditorTools;
|
|
using UnityEditor.Overlays;
|
|
using UnityEditor.ProBuilder.Actions;
|
|
using UnityEditor.SettingsManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.ProBuilder;
|
|
using UnityEngine.Profiling;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace UnityEditor.ProBuilder
|
|
{
|
|
class PreviewActionManager : IDisposable
|
|
{
|
|
[UserSetting("Mesh Editing", "Auto Update Action Preview", "Automatically update the action preview, without delay. This operation is costly and can cause lag when working with large selections.")]
|
|
static Pref<bool> s_AutoUpdatePreview = new Pref<bool>("editor.autoUpdatePreview", false, SettingsScope.Project);
|
|
internal static bool delayedPreview => !s_AutoUpdatePreview.value;
|
|
public static event Action delayedPreviewChanged;
|
|
|
|
MenuAction m_CurrentAction;
|
|
|
|
bool m_HasPreview;
|
|
internal static bool hasPreview => s_Instance?.m_HasPreview ?? false;
|
|
|
|
bool m_SelectionUpdateDisabled = false;
|
|
internal static bool selectionUpdateDisabled
|
|
{
|
|
set
|
|
{
|
|
if (s_Instance != null)
|
|
s_Instance.m_SelectionUpdateDisabled = value;
|
|
}
|
|
}
|
|
|
|
Overlay m_Overlay;
|
|
|
|
static PreviewActionManager s_Instance;
|
|
internal static string actionName => s_Instance?.m_CurrentAction?.menuTitle ?? "";
|
|
internal static string actionTooltip => s_Instance?.m_CurrentAction?.tooltip.summary ?? "";
|
|
|
|
void Init(MenuAction action, bool preview)
|
|
{
|
|
m_CurrentAction = action;
|
|
m_HasPreview = preview;
|
|
|
|
if (preview)
|
|
{
|
|
UndoUtility.StartPreview();
|
|
m_CurrentAction.PerformAction();
|
|
}
|
|
|
|
// Changing selection/tool/context should apply the preview and exit the current action
|
|
Selection.selectionChanged += ObjectSelectionChanged;
|
|
ToolManager.activeContextChanged += Validate;
|
|
ToolManager.activeToolChanged += Validate;
|
|
ProBuilderEditor.selectionUpdated += OnSelectionUpdated;
|
|
ProBuilderEditor.selectModeChanged += SelectModeChanged;
|
|
|
|
SceneView.AddOverlayToActiveView(m_Overlay = new MenuActionSettingsOverlay());
|
|
// Hack to ensure that the overlay is displayed in the sceneview even when the overlay was already displayed before
|
|
// Doing only displayed = true is not adding the overlay as it should in the view, only changing the display status is
|
|
m_Overlay.displayed = false;
|
|
m_Overlay.displayed = true;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
s_Instance = null;
|
|
SceneView.RemoveOverlayFromActiveView(m_Overlay);
|
|
|
|
ProBuilderEditor.selectionUpdated -= OnSelectionUpdated;
|
|
ProBuilderEditor.selectModeChanged -= SelectModeChanged;
|
|
ToolManager.activeContextChanged -= Validate;
|
|
ToolManager.activeToolChanged -= Validate;
|
|
Selection.selectionChanged -= ObjectSelectionChanged;
|
|
|
|
UndoUtility.ExitAndValidatePreview();
|
|
}
|
|
|
|
internal static bool IsCurrentAction(MenuAction action)
|
|
{
|
|
if(s_Instance == null || s_Instance.m_CurrentAction == null)
|
|
return false;
|
|
|
|
return s_Instance.m_CurrentAction.GetType() == action.GetType();
|
|
}
|
|
|
|
internal static void DoAction(MenuAction action, bool preview)
|
|
{
|
|
Validate();
|
|
s_Instance = new PreviewActionManager();
|
|
s_Instance.Init(action, preview);
|
|
}
|
|
|
|
internal static VisualElement GetContent()
|
|
{
|
|
return s_Instance?.m_CurrentAction?.CreateSettingsContent() ?? new VisualElement();
|
|
}
|
|
|
|
internal static void SetPreviewUpdate(bool value)
|
|
{
|
|
if(s_AutoUpdatePreview.value == value)
|
|
return;
|
|
|
|
s_AutoUpdatePreview.value = value;
|
|
|
|
delayedPreviewChanged?.Invoke();
|
|
}
|
|
|
|
internal static void EndPreview()
|
|
{
|
|
s_Instance?.Dispose();
|
|
}
|
|
|
|
[InitializeOnEnterPlayMode]
|
|
internal static void ResetPreviewActionManagerStatics()
|
|
{
|
|
Cancel();
|
|
}
|
|
|
|
internal static void Validate()
|
|
{
|
|
s_Instance?.ValidateInternal();
|
|
}
|
|
|
|
void ValidateInternal()
|
|
{
|
|
Dispose();
|
|
if (m_HasPreview)
|
|
UndoUtility.ExitAndValidatePreview();
|
|
else
|
|
m_CurrentAction.PerformAction();
|
|
}
|
|
|
|
internal static void Cancel()
|
|
{
|
|
s_Instance?.CancelInternal();
|
|
}
|
|
|
|
void CancelInternal()
|
|
{
|
|
Dispose();
|
|
if(m_HasPreview)
|
|
UndoUtility.UndoPreview();
|
|
}
|
|
|
|
void ObjectSelectionChanged()
|
|
{
|
|
if (!m_SelectionUpdateDisabled)
|
|
Validate();
|
|
|
|
m_SelectionUpdateDisabled = false;
|
|
}
|
|
|
|
void SelectModeChanged(SelectMode _) => Validate();
|
|
|
|
void OnSelectionUpdated(IEnumerable<ProBuilderMesh> _)
|
|
{
|
|
Validate();
|
|
}
|
|
|
|
internal static void UpdatePreview()
|
|
{
|
|
s_Instance?.UpdatePreviewInternal();
|
|
}
|
|
|
|
internal void UpdatePreviewInternal()
|
|
{
|
|
//Undo action might be triggering a refresh of the mesh and of the selection, so we need to temporarily unregister to these events
|
|
ProBuilderEditor.selectionUpdated -= OnSelectionUpdated;
|
|
Selection.selectionChanged -= ObjectSelectionChanged;
|
|
UndoUtility.StartPreview();
|
|
m_CurrentAction.PerformAction();
|
|
ProBuilderEditor.selectionUpdated += OnSelectionUpdated;
|
|
Selection.selectionChanged += ObjectSelectionChanged;
|
|
}
|
|
}
|
|
|
|
class MenuActionSettingsOverlay : Overlay
|
|
{
|
|
public MenuActionSettingsOverlay()
|
|
{
|
|
displayName = PreviewActionManager.actionName;
|
|
}
|
|
|
|
public override VisualElement CreatePanelContent()
|
|
{
|
|
rootVisualElement.tooltip = PreviewActionManager.actionTooltip;
|
|
|
|
var root = new VisualElement();
|
|
root.style.flexDirection = FlexDirection.Column;
|
|
root.style.minWidth = root.style.maxWidth = 300;
|
|
|
|
var lastLine = new VisualElement();
|
|
lastLine.style.flexDirection = FlexDirection.Row;
|
|
var okButton = new Button(PreviewActionManager.Validate);
|
|
okButton.text = "Confirm";
|
|
okButton.style.flexGrow = 1;
|
|
var cancelButton = new Button(PreviewActionManager.Cancel);
|
|
cancelButton.text = "Cancel";
|
|
cancelButton.style.flexGrow = 1;
|
|
lastLine.Add(okButton);
|
|
lastLine.Add(cancelButton);
|
|
|
|
var settingsElement = PreviewActionManager.GetContent();
|
|
root.Add(settingsElement);
|
|
if (PreviewActionManager.hasPreview)
|
|
{
|
|
var toggle = new Toggle("Live Preview");
|
|
toggle.SetValueWithoutNotify(!PreviewActionManager.delayedPreview);
|
|
toggle.RegisterCallback<ChangeEvent<bool>>(evt =>
|
|
{
|
|
PreviewActionManager.SetPreviewUpdate(evt.newValue);
|
|
});
|
|
root.Add(toggle);
|
|
}
|
|
|
|
root.Add(lastLine);
|
|
return root;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// An EditorAction for displaying MenuAction settings overlay and action previewing.
|
|
/// </summary>
|
|
public class MenuActionSettings : EditorAction
|
|
{
|
|
static bool s_CanTriggerNewAction = true;
|
|
|
|
[InitializeOnEnterPlayMode]
|
|
static void ResetMenuActionSettingsStatics()
|
|
{
|
|
s_CanTriggerNewAction = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// MenuActionSettings constructor.
|
|
/// </summary>
|
|
/// <param name="action">The MenuAction for which to display a settings overlay.</param>
|
|
/// <param name="hasPreview">Indicates if the action can be previewed.</param>
|
|
public MenuActionSettings(MenuAction action, bool hasPreview = false)
|
|
{
|
|
if (!s_CanTriggerNewAction)
|
|
{
|
|
Finish(EditorActionResult.Canceled);
|
|
return;
|
|
}
|
|
|
|
s_CanTriggerNewAction = false;
|
|
PreviewActionManager.DoAction(action, hasPreview);
|
|
|
|
// Ensure we are not calling the action again in the same frame
|
|
// Needed because Menu Items are called once per selected object while PB actions are acting on the entire selection at once
|
|
EditorApplication.delayCall += () => { s_CanTriggerNewAction = true; };
|
|
|
|
Finish(EditorActionResult.Success);
|
|
}
|
|
|
|
internal static bool IsCurrentAction(MenuAction action)
|
|
{
|
|
return PreviewActionManager.IsCurrentAction(action);
|
|
}
|
|
}
|
|
}
|