-
Notifications
You must be signed in to change notification settings - Fork 857
Rework toolbar design #5706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework toolbar design #5706
Changes from all commits
be358b1
1c33600
657da4e
d3f71ab
c47b092
3298f90
465ac3f
45d6fc8
ceb838d
eb65d1b
108a1f3
0739519
72ea906
cd3ca14
8a2a97e
4d796e6
8c96576
f3c03e6
43ea1d6
5127a63
43e3122
f5242fb
af848d1
af37f36
4c03eba
3958aa3
a84f53f
bd0cd62
a7e70c5
4ebf9d0
148352e
6aeee5c
888c0c1
645230d
9fb44f2
09dc442
c070d07
3f6233c
7008a0b
c6f4e64
44c9d40
39ce14d
4ed6177
3f83b3b
16e2e4f
294a443
799b47a
49a60e7
353aeb0
2cdd218
31d136d
32c2ff5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,10 @@ namespace UnityEditor.VFX.UI | |
| [Serializable] | ||
| class VFXViewWindow : EditorWindow | ||
| { | ||
| static VisualEffect s_LastAttachedComponent; | ||
|
|
||
| ShortcutHandler m_ShortcutHandler; | ||
|
|
||
| protected void SetupFramingShortcutHandler(VFXView view) | ||
| { | ||
| m_ShortcutHandler = new ShortcutHandler( | ||
|
|
@@ -29,7 +32,7 @@ protected void SetupFramingShortcutHandler(VFXView view) | |
| {Event.KeyboardEvent("o"), view.FrameOrigin }, | ||
| {Event.KeyboardEvent("^#>"), view.FramePrev }, | ||
| {Event.KeyboardEvent("^>"), view.FrameNext }, | ||
| {Event.KeyboardEvent("F7"), view.Compile}, | ||
| {Event.KeyboardEvent("F7"), view.OnCompile}, | ||
| {Event.KeyboardEvent("#d"), view.OutputToDot}, | ||
| {Event.KeyboardEvent("^&d"), view.DuplicateSelectionWithEdges}, | ||
| {Event.KeyboardEvent("^#d"), view.OutputToDotReduced}, | ||
|
|
@@ -41,7 +44,7 @@ protected void SetupFramingShortcutHandler(VFXView view) | |
| }); | ||
| } | ||
|
|
||
| public static VFXViewWindow currentWindow; | ||
| public static VFXViewWindow currentWindow => WindowLayout.FindEditorWindowOfType(typeof(VFXViewWindow)) as VFXViewWindow; | ||
|
|
||
| [MenuItem("Window/Visual Effects/Visual Effect Graph", false, 3011)] | ||
| public static void ShowWindow() | ||
|
|
@@ -80,8 +83,11 @@ public void LoadResource(VisualEffectResource resource, VisualEffect effectToAtt | |
| { | ||
| InternalLoadResource(resource); | ||
| } | ||
| if (effectToAttach != null && graphView.controller != null && graphView.controller.model != null && effectToAttach.visualEffectAsset == graphView.controller.model.asset) | ||
| graphView.attachedComponent = effectToAttach; | ||
|
|
||
| if (!graphView.TryAttachTo(effectToAttach)) | ||
| { | ||
| s_LastAttachedComponent = null; | ||
| } | ||
| } | ||
|
|
||
| List<VisualEffectResource> m_ResourceHistory = new List<VisualEffectResource>(); | ||
|
|
@@ -106,6 +112,12 @@ void InternalLoadResource(VisualEffectResource resource) | |
| graphView.controller = VFXViewController.GetController(resource, true); | ||
| graphView.UpdateGlobalSelection(); | ||
| graphView.FrameNewController(); | ||
| graphView.UpdateIsSubgraph(); | ||
| } | ||
|
|
||
| public bool CanPopResource() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I introduced a "back" button when we entered a subgraph, this is to know if the button should be displayed or not |
||
| { | ||
| return m_ResourceHistory.Any(); | ||
| } | ||
|
|
||
| public void PopResource() | ||
|
|
@@ -174,7 +186,7 @@ protected void CreateGUI() | |
| var currentAsset = GetCurrentResource(); | ||
| if (currentAsset != null) | ||
| { | ||
| LoadResource(currentAsset); | ||
| LoadResource(currentAsset, s_LastAttachedComponent); | ||
| } | ||
| }; | ||
|
|
||
|
|
@@ -188,8 +200,6 @@ protected void CreateGUI() | |
| rootVisualElement.AddManipulator(m_ShortcutHandler); | ||
| } | ||
|
|
||
| currentWindow = this; | ||
|
|
||
| #if USE_EXIT_WORKAROUND_FOGBUGZ_1062258 | ||
| EditorApplication.wantsToQuit += Quitting_Workaround; | ||
| #endif | ||
|
|
@@ -216,11 +226,11 @@ protected void OnDestroy() | |
|
|
||
| if (graphView != null) | ||
| { | ||
| s_LastAttachedComponent = graphView.attachedComponent; | ||
| graphView.UnregisterCallback<AttachToPanelEvent>(OnEnterPanel); | ||
| graphView.UnregisterCallback<DetachFromPanelEvent>(OnLeavePanel); | ||
| graphView.controller = null; | ||
| } | ||
| currentWindow = null; | ||
| } | ||
|
|
||
| void OnEnterPanel(AttachToPanelEvent e) | ||
|
|
@@ -309,19 +319,6 @@ void Update() | |
| VFXViewModificationProcessor.assetMoved = false; | ||
| } | ||
| titleContent.text = filename; | ||
|
|
||
| if (graphView?.controller?.model?.visualEffectObject != null) | ||
| { | ||
| graphView.checkoutButton.visible = true; | ||
| if (!graphView.IsAssetEditable() && Provider.isActive && Provider.enabled) | ||
| { | ||
| graphView.checkoutButton.SetEnabled(true); | ||
| } | ||
| else | ||
| { | ||
| graphView.checkoutButton.SetEnabled(false); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [SerializeField] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| using System; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a reusable class to add dropdowns (a button and an arrow button to open a popup). |
||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
|
|
||
| using UnityEngine; | ||
| using UnityEngine.UIElements; | ||
|
|
||
| namespace UnityEditor.VFX.UI | ||
| { | ||
| abstract class DropDownButtonBase : VisualElement | ||
| { | ||
| private class NotifyEditorWindow : EditorWindow | ||
| { | ||
| public event Action Destroyed; | ||
|
|
||
| private void OnDestroy() | ||
| { | ||
| Destroyed?.Invoke(); | ||
| } | ||
| } | ||
|
|
||
| readonly bool m_HasLeftSeparator; | ||
| readonly Button m_MainButton; | ||
|
|
||
| NotifyEditorWindow m_CurrentPopup; | ||
| DateTime m_PopupClosedTimestamp; | ||
|
|
||
| protected readonly VisualElement m_PopupContent; | ||
| protected readonly VFXViewWindow m_ParentWindow; | ||
|
|
||
| protected DropDownButtonBase( | ||
| VFXViewWindow parentWindow, | ||
| string uxmlSource, | ||
| string mainButtonLabel, | ||
| string mainButtonName, | ||
| string iconPath, | ||
| bool hasSeparatorBefore = false, | ||
| bool hasSeparatorAfter = false) | ||
| { | ||
| m_ParentWindow = parentWindow; | ||
| style.flexDirection = new StyleEnum<FlexDirection>(FlexDirection.Row); | ||
|
|
||
| if (hasSeparatorBefore) | ||
| { | ||
| m_HasLeftSeparator = true; | ||
| var divider = new VisualElement(); | ||
| divider.AddToClassList("separator"); | ||
| Add(divider); | ||
| } | ||
|
|
||
| m_MainButton = new Button(OnMainButton) { name = mainButtonName }; | ||
| m_MainButton.AddToClassList("dropdown-button"); | ||
| m_MainButton.AddToClassList("unity-toolbar-toggle"); | ||
| if (!string.IsNullOrEmpty(iconPath)) | ||
| { | ||
| var icon = new Image { image = EditorGUIUtility.LoadIcon(iconPath) }; | ||
| m_MainButton.Add(icon); | ||
| m_MainButton.tooltip = mainButtonLabel; | ||
| } | ||
| else | ||
| { | ||
| m_MainButton.text = mainButtonLabel; | ||
| } | ||
| Add(m_MainButton); | ||
|
|
||
| var separator = new VisualElement(); | ||
| separator.AddToClassList("dropdown-separator"); | ||
| Add(separator); | ||
|
|
||
| var dropDownButton = new Button(OnTogglePopup); | ||
| dropDownButton.AddToClassList("dropdown-arrow"); | ||
| dropDownButton.AddToClassList("unity-toolbar-toggle"); | ||
| dropDownButton.Add(new VisualElement()); | ||
| Add(dropDownButton); | ||
|
|
||
| if (hasSeparatorAfter) | ||
| { | ||
| var divider = new VisualElement(); | ||
| divider.AddToClassList("separator"); | ||
| Add(divider); | ||
| } | ||
|
|
||
| m_PopupContent = new VisualElement(); | ||
| var tpl = VFXView.LoadUXML(uxmlSource); | ||
| tpl.CloneTree(m_PopupContent); | ||
| contentContainer.AddStyleSheetPath("VFXToolbar"); | ||
| } | ||
|
|
||
| protected virtual void OnOpenPopup() { } | ||
| protected virtual void OnMainButton() { } | ||
| protected abstract Vector2 GetPopupSize(); | ||
|
|
||
| protected void ClosePopup() | ||
| { | ||
| m_CurrentPopup?.Close(); | ||
| } | ||
|
|
||
| private Vector2 GetPopupPosition() => m_ParentWindow.graphView.ViewToScreenPosition(worldBound.position); | ||
|
|
||
| private void OnTogglePopup() | ||
| { | ||
| // If the user click on the arrow button while the popup is opened | ||
| // the popup is then closed (because clicked outside) and immediately reopened | ||
| // To prevent this behavior we allow the popup to reopen only after a very short period of time after being closed | ||
| var deltaTime = DateTime.UtcNow - m_PopupClosedTimestamp; | ||
| if (m_CurrentPopup == null && deltaTime.TotalMilliseconds > 500) | ||
| { | ||
| m_CurrentPopup = ScriptableObject.CreateInstance<NotifyEditorWindow>(); | ||
| m_CurrentPopup.hideFlags = HideFlags.HideAndDontSave; | ||
| if (m_PopupContent.parent != null) | ||
| { | ||
| m_PopupContent.parent.Remove(m_PopupContent); | ||
| } | ||
|
|
||
| m_CurrentPopup.Destroyed += OnPopupClosed; | ||
| m_CurrentPopup.rootVisualElement.AddStyleSheetPath("VFXToolbar"); | ||
| m_CurrentPopup.rootVisualElement.Add(m_PopupContent); | ||
| m_CurrentPopup.rootVisualElement.AddToClassList("popup"); | ||
| m_CurrentPopup.rootVisualElement.RegisterCallback<KeyUpEvent>(OnKeyUp); | ||
|
|
||
| OnOpenPopup(); | ||
| var bounds = new Rect(GetPopupPosition(), localBound.size); | ||
| // Offset the bounds to align the popup with the real dropdown left edge | ||
| if (m_HasLeftSeparator) | ||
| { | ||
| bounds.xMin += 6; | ||
| } | ||
|
|
||
| m_CurrentPopup.ShowAsDropDown(bounds, GetPopupSize(), new[] { PopupLocation.BelowAlignLeft, PopupLocation.AboveAlignLeft }); | ||
| m_CurrentPopup.minSize = GetPopupSize(); | ||
| m_CurrentPopup.maxSize = m_CurrentPopup.minSize; | ||
| GetNextFocusable(null, m_PopupContent.Children(), false)?.Focus(); | ||
| } | ||
| else if (m_CurrentPopup != null) | ||
| { | ||
| ClosePopup(); | ||
| } | ||
| } | ||
|
|
||
| private void OnPopupClosed() | ||
| { | ||
| m_CurrentPopup.Destroyed -= OnPopupClosed; | ||
| m_CurrentPopup.rootVisualElement.UnregisterCallback<KeyUpEvent>(OnKeyUp); | ||
| m_CurrentPopup = null; | ||
| m_PopupClosedTimestamp = DateTime.UtcNow; | ||
| } | ||
|
|
||
| private void OnKeyUp(KeyUpEvent evt) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These last three methods were added to handle keyboard navigation (either with Tab or arrow keys). This was requested by UX designers |
||
| { | ||
| var focused = m_PopupContent.focusController.focusedElement; | ||
| switch (evt.keyCode) | ||
| { | ||
| case KeyCode.DownArrow: | ||
| var next = GetNextFocusable(focused, m_PopupContent.Children(), false); | ||
| next?.Focus(); | ||
| break; | ||
| case KeyCode.UpArrow: | ||
| var prev = GetNextFocusable(focused, m_PopupContent.Children(), true); | ||
| prev?.Focus(); | ||
| break; | ||
| case KeyCode.Escape: | ||
| ClosePopup(); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| private VisualElement GetNextFocusable(Focusable focused, IEnumerable<VisualElement> elements, bool reverse) | ||
| { | ||
| var found = focused == null; | ||
| return GetNextFocusableRecursive(focused, elements, reverse, ref found); | ||
| } | ||
|
|
||
| private VisualElement GetNextFocusableRecursive(Focusable focused, IEnumerable<VisualElement> elements, bool reverse, ref bool found) | ||
| { | ||
| var collection = reverse ? elements.Reverse() : elements; | ||
| foreach (var child in collection) | ||
| { | ||
| if (child == focused) | ||
| { | ||
| found = true; | ||
| continue; | ||
| } | ||
|
|
||
| if (found && child != focused && child.enabledSelf && child.focusable) | ||
| { | ||
| return child; | ||
| } | ||
|
|
||
| var next = GetNextFocusableRecursive(focused, child.Children(), reverse, ref found); | ||
| if (next != null) | ||
| { | ||
| return next; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The introduction of this static variable was done to fix a bug when "unmaximizing" the window. Because in that process the window is recreated and then losing the attachment.