Added player character and step in/out of dialogue in scene 101

This commit is contained in:
2026-08-15 15:22:47 +02:00
parent a42f25d5fa
commit 38ba2b0eff
2019 changed files with 181227 additions and 265 deletions
@@ -0,0 +1,267 @@
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);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 04a63eaa34df4781afea8b61f4bf6a34
timeCreated: 1687809530
@@ -0,0 +1,417 @@
using System;
using System.Collections.Generic;
using UnityEditor.Actions;
using UnityEditor.EditorTools;
using UnityEditor.Overlays;
using UnityEditor.ProBuilder.Actions;
using UnityEditor.Search;
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.UIElements;
using UnityEditor.SettingsManagement;
namespace UnityEditor.ProBuilder
{
class ProBuilderActionButton : VisualElement
{
const string k_UxmlPath = "Packages/com.unity.probuilder/Editor/Overlays/UXML/ActionButton.uxml";
static VisualTreeAsset s_ButtonAsset;
bool m_Initialized = false;
MenuAction m_Action;
private MenuAction action
{
get => m_Action;
set
{
m_Action = value;
m_IconTexture = null;
}
}
Button m_Button;
Label m_Label;
VisualElement m_Color;
Image m_Icon;
Action m_ClickHandler;
Texture2D m_IconTexture = null;
Texture2D iconTexture
{
get
{
if (m_IconTexture == null && m_Action != null)
m_IconTexture = m_Action.icon;
if (m_IconTexture == null)
m_IconTexture = IconUtility.GetIcon("Tools/EditShape");
return m_IconTexture;
}
}
internal ProBuilderActionButton()
{
Initialize();
}
internal ProBuilderActionButton(MenuAction act) : this()
{
Bind(act);
}
void Initialize()
{
if (s_ButtonAsset == null)
s_ButtonAsset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_UxmlPath);
if (s_ButtonAsset != null)
{
m_Initialized = true;
s_ButtonAsset.CloneTree(this);
m_Button = this.Q<Button>();
m_Label = this.Q<Label>();
m_Color = this.Q<VisualElement>("ActionColor");
m_Icon = this.Q<Image>("ActionIcon");
}
}
internal void Bind(MenuAction act)
{
if (!m_Initialized)
Initialize();
action = act;
if (m_Initialized)
{
if (m_ClickHandler != null)
m_Button.clicked -= m_ClickHandler;
m_ClickHandler = () => EditorAction.Start(new MenuActionSettings(action, HasPreview(action)));
m_Button.clicked += m_ClickHandler;
m_Color.style.backgroundColor = ToolbarGroupUtility.GetColor(m_Action.group);
m_Label.text = action.menuTitle;
m_Icon.image = iconTexture;
}
}
static bool HasPreview(MenuAction action)
{
return !(action is DetachFaces || action is DuplicateFaces);
}
void CleanUpStyles()
{
//Remove all styles
m_Button?.RemoveFromClassList("toolbarHorizontalMode");
m_Button?.RemoveFromClassList("toolbarVerticalMode");
m_Button?.RemoveFromClassList("enabledAction");
m_Button?.RemoveFromClassList("unity-overlay");
m_Button?.RemoveFromClassList("unity-toolbar-toggle");
m_Label?.RemoveFromClassList("toolbarMode");
m_Color?.RemoveFromClassList("toolbarMode");
m_Icon?.RemoveFromClassList("toolbarMode");
}
internal void UpdateContent()
{
if (!m_Initialized)
return;
style.flexGrow = 1f;
m_Button.style.flexGrow = 1f;
m_Button.enabledSelf = m_Action.enabled;
m_Button.tooltip = m_Action.menuTitle;
m_Icon.style.display = DisplayStyle.Flex;
m_Label.style.display = DisplayStyle.Flex;
CleanUpStyles();
if(m_Action.enabled)
m_Button.AddToClassList("enabledAction");
m_Button.AddToClassList("unity-overlay");
m_Button.AddToClassList("unity-toolbar-toggle");
m_Icon.style.display = ProBuilderActionsOverlay.s_CurrentMode == ProBuilderActionsOverlay.DisplayMode.Text ? DisplayStyle.None : DisplayStyle.Flex;
m_Label.style.display = ProBuilderActionsOverlay.s_CurrentMode == ProBuilderActionsOverlay.DisplayMode.Icon ? DisplayStyle.None : DisplayStyle.Flex;
}
internal void UpdateContentForToolbar(Layout layout)
{
if (!m_Initialized)
return;
var hidden = m_Action.hidden;
var isGOContext = EditorToolManager.activeToolContext is GameObjectToolContext;
hidden |= (m_Action.group == ToolbarGroup.Object) ? !isGOContext : isGOContext;
m_Button.style.display = hidden ? DisplayStyle.None : DisplayStyle.Flex;
m_Button.enabledSelf = m_Action.enabled;
m_Button.tooltip = m_Action.menuTitle;
CleanUpStyles();
if(layout == Layout.HorizontalToolbar)
m_Button.AddToClassList("toolbarHorizontalMode");
else
m_Button.AddToClassList("toolbarVerticalMode");
if(m_Action.enabled)
m_Button.AddToClassList("enabledAction");
m_Button.AddToClassList("unity-overlay");
m_Button.AddToClassList("unity-toolbar-toggle");
m_Label.AddToClassList("toolbarMode");
m_Color.AddToClassList("toolbarMode");
m_Icon.AddToClassList("toolbarMode");
}
}
[Overlay(typeof(SceneView), overlayId, k_DisplayName, minHeight = 150f, maxHeight = 500f, minWidth = 200f, maxWidth = 600f, defaultDisplay = false)]
[Icon("Packages/com.unity.probuilder/Editor Default Resources/Icons/EditableMesh/EditMeshContext.png")]
class ProBuilderActionsOverlay : Overlay, ICreateHorizontalToolbar, ICreateVerticalToolbar
{
const string k_DisplayName = "ProBuilder Actions";
private const string overlayId = "ProBuilder/ActionsOverlay";
private static readonly HashSet<Type> k_ContextMenuBlacklist = new HashSet<Type>()
{
typeof(ToggleHandleOrientation),
typeof(ToggleDragRectMode),
typeof(ToggleSelectBackFaces),
typeof(NewBezierShape)
};
private List<MenuAction> m_Actions;
private List<MenuAction> m_AvailableActions = new ();
List<ProBuilderActionButton> m_ActionButtons = new List<ProBuilderActionButton>();
GridView m_Grid;
VisualElement m_ContentViewport;
OverlayToolbar m_Toolbar;
public enum DisplayMode
{
Icon,
Text,
Full
}
[UserSetting("Actions Overlay", "Display Mode", "Change the display mode of ProBuilder actions in the overlay when using Panel mode")]
internal static Pref<DisplayMode> s_CurrentMode = new Pref<DisplayMode>("pb_overlay.displaymode", DisplayMode.Full, SettingsScope.User);
[UserSetting("Actions Overlay", "Display Editors", "Toggle the display of editors actions in the ProBuilder overlay")]
static Pref<bool> s_DisplayEditors = new Pref<bool>("pb_overlay.displayeditors", true, SettingsScope.User);
[UserSetting("Actions Overlay", "Display Selection", "Toggle the display of selection actions in the ProBuilder overlay")]
static Pref<bool> s_DisplaySelection = new Pref<bool>("pb_overlay.displayselection", false, SettingsScope.User);
public ProBuilderActionsOverlay()
{
m_Actions = EditorToolbarLoader.GetActions();
RefreshAvailableActions();
rootVisualElement.RegisterCallback<AttachToPanelEvent>(OnAttachedToPanel);
rootVisualElement.RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
layoutChanged += _ => UpdateContent();
floatingChanged += _ => UpdateContent();
dockingCompleted += _ => UpdateContent();
}
private void OnAttachedToPanel(AttachToPanelEvent evt)
{
Selection.selectionChanged += UpdateContent;
ProBuilderEditor.selectModeChanged += OnSelectModeChanged;
ProBuilderEditor.selectionUpdated += OnSelectionUpdated;
ToolManager.activeContextChanged += UpdateContent;
rootVisualElement.RegisterCallback<GeometryChangedEvent>(OnGeometryChangedEvent);
}
private void OnDetachFromPanel(DetachFromPanelEvent evt)
{
Selection.selectionChanged -= UpdateContent;
ProBuilderEditor.selectModeChanged -= OnSelectModeChanged;
ProBuilderEditor.selectionUpdated -= OnSelectionUpdated;
ToolManager.activeContextChanged -= UpdateContent;
rootVisualElement.UnregisterCallback<GeometryChangedEvent>(OnGeometryChangedEvent);
}
void UpdateContent()
{
RefreshAvailableActions();
UpdateGrid();
UpdateToolbar();
}
private void OnSelectModeChanged(SelectMode obj) => UpdateContent();
private void OnSelectionUpdated(IEnumerable<ProBuilderMesh> obj) => UpdateContent();
void OnGeometryChangedEvent(GeometryChangedEvent _)
{
UpdateGrid();
}
public override VisualElement CreatePanelContent()
{
var root = new VisualElement();
root.name = "ProbuilderActions";
m_Grid = new GridView(m_AvailableActions, 0, 0, MakeItem, BindItem);
m_ContentViewport = m_Grid.Q("unity-content-viewport");
root.Add(m_Grid);
OnSelectModeChanged(ProBuilderEditor.selectMode);
var contextClickGrid = new ContextualMenuManipulator(BuildContextMenu);
m_Grid.AddManipulator(contextClickGrid);
return root;
}
private VisualElement MakeItem()
{
return new ProBuilderActionButton();
}
private void BindItem(VisualElement element, int index)
{
var e = (ProBuilderActionButton)element;
if (index >= 0 && index < m_AvailableActions.Count)
{
e.Bind(m_AvailableActions[index]);
e.UpdateContent();
e.style.flexGrow = 1f;
}
}
private void RefreshAvailableActions()
{
m_AvailableActions.Clear();
var initActionButtons = m_ActionButtons.Count == 0;
if (!initActionButtons)
{
foreach (var element in m_ActionButtons)
element.style.display = DisplayStyle.None;
}
int actionIndex = 0;
var isGOContext = EditorToolManager.activeToolContext is GameObjectToolContext;
for (int i = 0; i < m_Actions.Count; i++)
{
var action = m_Actions[i];
if (k_ContextMenuBlacklist.Contains(action.GetType()))
continue;
if (action.group == ToolbarGroup.Entity)
continue;
var shouldDisplayAsEditor = s_DisplayEditors && action.group == ToolbarGroup.Tool;
var shouldDisplayAsSelection = s_DisplaySelection && action.group == ToolbarGroup.Selection;
var shouldDisplay = action.group != ToolbarGroup.Tool && action.group != ToolbarGroup.Selection;
var hidden = action.hidden;
hidden |= (action.group == ToolbarGroup.Object) ? !isGOContext : isGOContext;
if (initActionButtons)
m_ActionButtons.Add( new ProBuilderActionButton(action) );
if (!hidden)
{
if (shouldDisplayAsEditor || shouldDisplayAsSelection || shouldDisplay)
{
m_AvailableActions.Add(action);
m_ActionButtons[actionIndex].style.display = DisplayStyle.Flex;
}
}
actionIndex++;
}
}
private void UpdateGrid()
{
if (m_Grid != null)
{
m_Grid.itemsSource = m_AvailableActions;
var fullWidth = float.IsFinite(m_ContentViewport.resolvedStyle.width) ?
Mathf.Clamp(m_ContentViewport.resolvedStyle.width - 10f, 0f, 180f) : 180f;
m_Grid.fixedItemWidth = s_CurrentMode == DisplayMode.Icon ? 40f : fullWidth;
m_Grid.Rebuild();
m_Grid.RefreshItems();
}
}
void BuildContextMenu(ContextualMenuPopulateEvent evt)
{
var menu = evt.menu;
if (layout == Layout.Panel)
{
menu.AppendAction(L10n.Tr("Icon Mode"), _ => { SetMode(DisplayMode.Icon); },
s_CurrentMode == DisplayMode.Icon ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
menu.AppendAction(L10n.Tr("Text Mode"), _ => { SetMode(DisplayMode.Text); },
s_CurrentMode == DisplayMode.Text ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
menu.AppendAction(L10n.Tr("Text & Icon Mode"), _ => { SetMode(DisplayMode.Full); },
s_CurrentMode == DisplayMode.Full ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
menu.AppendSeparator();
}
menu.AppendAction(L10n.Tr("Display Editors Actions"), _ =>
{
s_DisplayEditors.SetValue(!s_DisplayEditors);
UpdateContent();
},
s_DisplayEditors ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
menu.AppendAction(L10n.Tr("Display Select Actions"), _ =>
{
s_DisplaySelection.SetValue(!s_DisplaySelection);
UpdateContent();
},
s_DisplaySelection ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
}
private void SetMode(DisplayMode mode)
{
s_CurrentMode.SetValue(mode);
UpdateContent();
}
public OverlayToolbar CreateHorizontalToolbarContent() => CreateToolbarOverlay();
public OverlayToolbar CreateVerticalToolbarContent() => CreateToolbarOverlay();
OverlayToolbar CreateToolbarOverlay()
{
if (m_Toolbar == null)
{
m_Toolbar = new OverlayToolbar();
var contextClick = new ContextualMenuManipulator(BuildContextMenu);
m_Toolbar.AddManipulator(contextClick);
}
else
m_Toolbar.Clear();
UpdateContent();
return m_Toolbar;
}
void UpdateToolbar()
{
if (m_Toolbar != null)
{
foreach (var proBuilderAction in m_ActionButtons)
{
proBuilderAction.UpdateContentForToolbar(layout);
m_Toolbar.Add(proBuilderAction);
}
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 38bf16cc593d8244ba3e84f7339f7429
@@ -0,0 +1,64 @@
using UnityEditor.Overlays;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.ProBuilder.Overlays
{
//[Overlay(typeof(SceneView), "Scene Information", true)]
class SceneInformationOverlay : Overlay
{
Label m_FaceCountLabel = new Label();
Label m_TriCountLabel = new Label();
Label m_VertCountLabel = new Label();
Label m_SelectedFaceCountLabel = new Label();
Label m_SelectedEdgeCountLabel = new Label();
Label m_SelectedVertCountLabel = new Label();
public SceneInformationOverlay()
{
ProBuilderEditor.selectionUpdated += _ => UpdateSceneInfo();
MeshSelection.objectSelectionChanged += UpdateSceneInfo;
}
public override VisualElement CreatePanelContent()
{
displayName = "ProBuilder Information";
var root = new VisualElement();
root.style.flexDirection = FlexDirection.Column;
root.Add(CreateLabelLine(m_FaceCountLabel, "Faces:"));
root.Add(CreateLabelLine(m_TriCountLabel, "Triangles:"));
root.Add(CreateLabelLine(m_VertCountLabel, "Vertices:"));
var spacer = new VisualElement();
spacer.style.height = 10;
root.Add(spacer); // Spacer line
root.Add(CreateLabelLine(m_SelectedFaceCountLabel, "Selected Faces:"));
root.Add(CreateLabelLine(m_SelectedEdgeCountLabel, "Selected Edges:"));
root.Add(CreateLabelLine(m_SelectedVertCountLabel, "Selected Vertices:"));
UpdateSceneInfo();
return root;
}
void UpdateSceneInfo()
{
m_FaceCountLabel.text = MeshSelection.totalFaceCount.ToString();
m_TriCountLabel.text = MeshSelection.totalTriangleCountCompiled.ToString();
m_VertCountLabel.text = MeshSelection.totalCommonVertexCount + " (" + MeshSelection.totalVertexCountOptimized + ")";
m_SelectedFaceCountLabel.text = MeshSelection.selectedFaceCount.ToString();
m_SelectedEdgeCountLabel.text = MeshSelection.selectedEdgeCount.ToString();
m_SelectedVertCountLabel.text = MeshSelection.selectedSharedVertexCount + " (" + MeshSelection.selectedVertexCount+ ")";
}
VisualElement CreateLabelLine(Label targetLabel, string displayName)
{
var line = new VisualElement();
line.style.flexDirection = FlexDirection.Row;
line.Add(new Label(displayName));
targetLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
line.Add(targetLabel);
return line;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 89cfedddacb905348b6ed063982e554d
@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using UnityEditor.ProBuilder.Actions;
using UnityEditor.Toolbars;
using UnityEngine;
using UnityEngine.ProBuilder;
using UnityEngine.UIElements;
namespace UnityEditor.ProBuilder.UI
{
class SelectModeToggle : EditorToolbarToggle
{
SelectMode m_Mode;
public SelectModeToggle(SelectMode mode)
{
m_Mode = mode;
switch (mode)
{
case SelectMode.Face:
case SelectMode.TextureFace:
icon = IconUtility.GetIcon("Modes/Mode_Face");
tooltip = "Face Selection";
break;
case SelectMode.Edge:
case SelectMode.TextureEdge:
icon = IconUtility.GetIcon("Modes/Mode_Edge");
tooltip = "Edge Selection";
break;
case SelectMode.Vertex:
case SelectMode.TextureVertex:
icon = IconUtility.GetIcon("Modes/Mode_Vertex");
tooltip = "Vertex Selection";
break;
default:
throw new ArgumentOutOfRangeException(nameof(mode));
}
RegisterCallback<AttachToPanelEvent>(evt => { ProBuilderEditor.selectModeChanged += UpdateSelectMode; });
RegisterCallback<DetachFromPanelEvent>(evt => { ProBuilderEditor.selectModeChanged -= UpdateSelectMode; });
this.RegisterValueChangedCallback(evt =>
{
if(evt.newValue)
ProBuilderEditor.selectMode = m_Mode;
//Trying to set the edit mode to false: Not allowed
else
SetValueWithoutNotify(true);
});
UpdateSelectMode(ProBuilderEditor.selectMode);
}
void UpdateSelectMode(SelectMode mode) => SetValueWithoutNotify(m_Mode == mode);
}
[EditorToolbarElement("ProBuilder Settings/Select Mode")]
class SelectModeToolbar : VisualElement
{
public SelectModeToolbar()
{
Add(new SelectModeToggle(SelectMode.Vertex));
Add(new SelectModeToggle(SelectMode.Edge));
Add(new SelectModeToggle(SelectMode.Face));
EditorToolbarUtility.SetupChildrenAsButtonStrip(this);
}
}
[EditorToolbarElement("ProBuilder Settings/Drag Rect Mode")]
class DragRectModeToggle : EditorToolbarToggle
{
DragRectModeToggle()
{
var action = EditorToolbarLoader.GetInstance<ToggleDragRectMode>();
tooltip = action.tooltip.summary;
style.display = action.hidden ? DisplayStyle.None : DisplayStyle.Flex;
offIcon = action.icons[0];
onIcon = action.icons[1];
SetValueWithoutNotify(ProBuilderEditor.rectSelectMode == RectSelectMode.Complete);
RegisterCallback<ChangeEvent<bool>>(evt => ProBuilderEditor.rectSelectMode = evt.newValue ? RectSelectMode.Complete : RectSelectMode.Partial);
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
}
void OnAttachToPanel(AttachToPanelEvent evt)
{
ProBuilderEditor.selectModeChanged += SelectModeUpdated;
ProBuilderEditor.rectSelectModeChanged += UpdateVisual;
}
void OnDetachFromPanel(DetachFromPanelEvent evt)
{
ProBuilderEditor.selectModeChanged -= SelectModeUpdated;
ProBuilderEditor.rectSelectModeChanged -= UpdateVisual;
}
void SelectModeUpdated(SelectMode s)
{
style.display = EditorToolbarLoader.GetInstance<ToggleDragRectMode>().hidden ? DisplayStyle.None : DisplayStyle.Flex;
}
void UpdateVisual()
{
SetValueWithoutNotify(ProBuilderEditor.rectSelectMode == RectSelectMode.Complete);
}
}
[EditorToolbarElement("ProBuilder Settings/Select Back Faces")]
class SelectBackFacesToggle : EditorToolbarToggle
{
SelectBackFacesToggle()
{
var action = EditorToolbarLoader.GetInstance<ToggleSelectBackFaces>();
tooltip = action.tooltip.summary;
style.display = action.hidden ? DisplayStyle.None : DisplayStyle.Flex;
offIcon = action.icons[0];
onIcon = action.icons[1];
SetValueWithoutNotify(ProBuilderEditor.backfaceSelectionEnabled);
RegisterCallback<ChangeEvent<bool>>(evt => ProBuilderEditor.backfaceSelectionEnabled = evt.newValue);
RegisterCallback<AttachToPanelEvent>(OnAttachToPanel);
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanel);
}
void OnAttachToPanel(AttachToPanelEvent evt)
{
ProBuilderEditor.selectModeChanged += SelectModeUpdated;
ProBuilderEditor.backfaceSelectionEnabledChanged += UpdateVisual;
}
void OnDetachFromPanel(DetachFromPanelEvent evt)
{
ProBuilderEditor.selectModeChanged -= SelectModeUpdated;
ProBuilderEditor.backfaceSelectionEnabledChanged += UpdateVisual;
}
void SelectModeUpdated(SelectMode s)
{
style.display = EditorToolbarLoader.GetInstance<ToggleSelectBackFaces>().hidden ? DisplayStyle.None : DisplayStyle.Flex;
}
void UpdateVisual()
{
SetValueWithoutNotify(ProBuilderEditor.backfaceSelectionEnabled);
}
}
[EditorToolbarElement("ProBuilder Settings/Handle Orientation")]
class HandleOrientationDropdown : EditorToolbarDropdown
{
ToggleHandleOrientation m_MenuAction;
readonly List<GUIContent> m_OptionContents = new List<GUIContent>();
const string k_NormalRotationIconPath = "Packages/com.unity.probuilder/Editor Default Resources/Icons/Modes/ToolHandleElement.png";
public HandleOrientationDropdown()
{
m_MenuAction = EditorToolbarLoader.GetInstance<ToggleHandleOrientation>();
name = "Handle Rotation";
tooltip = m_MenuAction.tooltip.summary;
var content = UnityEditor.EditorGUIUtility.TrTextContent(ToggleHandleOrientation.tooltips[(int)HandleOrientation.World].title,
ToggleHandleOrientation.tooltips[(int)HandleOrientation.World].summary,
"ToolHandleGlobal");
m_OptionContents.Add(content);
content = UnityEditor.EditorGUIUtility.TrTextContent(ToggleHandleOrientation.tooltips[(int)HandleOrientation.ActiveObject].title,
ToggleHandleOrientation.tooltips[(int)HandleOrientation.ActiveObject].summary,
"ToolHandleLocal");
m_OptionContents.Add(content);
content = UnityEditor.EditorGUIUtility.TrTextContent(ToggleHandleOrientation.tooltips[(int)HandleOrientation.ActiveElement].title,
ToggleHandleOrientation.tooltips[(int)HandleOrientation.ActiveElement].summary,
k_NormalRotationIconPath);
m_OptionContents.Add(content);
RegisterCallback<AttachToPanelEvent>(AttachedToPanel);
RegisterCallback<DetachFromPanelEvent>(DetachedFromPanel);
clicked += OpenContextMenu;
RefreshElementContent();
style.display = m_MenuAction.hidden ? DisplayStyle.None : DisplayStyle.Flex;
ProBuilderEditor.selectModeChanged += (s) => SelectModeUpdated();
}
void OpenContextMenu()
{
var menu = new GenericMenu();
menu.AddItem(m_OptionContents[(int)HandleOrientation.World], m_MenuAction.handleOrientation == HandleOrientation.World,
() => SetHandleOrientationIfNeeded(HandleOrientation.World));
menu.AddItem(m_OptionContents[(int)HandleOrientation.ActiveObject], m_MenuAction.handleOrientation == HandleOrientation.ActiveObject,
() => SetHandleOrientationIfNeeded(HandleOrientation.ActiveObject));
menu.AddItem(m_OptionContents[(int)HandleOrientation.ActiveElement], m_MenuAction.handleOrientation == HandleOrientation.ActiveElement,
() => SetHandleOrientationIfNeeded(HandleOrientation.ActiveElement));
menu.DropDown(worldBound);
}
void SetHandleOrientationIfNeeded(HandleOrientation handleOrientation)
{
if (m_MenuAction.handleOrientation != handleOrientation)
{
m_MenuAction.handleOrientation = handleOrientation;
RefreshElementContent();
}
}
void RefreshElementContent()
{
var content = m_OptionContents[(int)m_MenuAction.handleOrientation];
text = content.text;
tooltip = content.tooltip;
icon = content.image as Texture2D;
}
void AttachedToPanel(AttachToPanelEvent evt)
=> VertexManipulationTool.handleOrientationChanged += RefreshElementContent;
void DetachedFromPanel(DetachFromPanelEvent evt)
=> VertexManipulationTool.handleOrientationChanged -= RefreshElementContent;
void SelectModeUpdated()
{
style.display = m_MenuAction.hidden ? DisplayStyle.None : DisplayStyle.Flex;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 82948eca45d445f6857c9c565e803d29
timeCreated: 1687792175
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0314a9d062fdabf4c892d13ba0372f4e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,114 @@
:root {
flex-grow: 1;
}
#PanelButton {
border-width: 1px;
border-color: transparent; /* Set to transparent to avoid layout shifts */
border-radius: 4px;
transition-duration: 0.2s; /* Smooth out the transition */
margin: 1px;
padding-right: 2px;
padding-left: 2px;
overflow: hidden;
}
#PanelButton.enabledAction:hover {
border-color: #0055CC; /* Change to your desired border color */
border-width: 1px;
background-color: #444444;
}
#PanelButton.toolbarHorizontalMode {
width: 32px;
height: 20px;
max-width: 32px;
max-height: 20px;
min-width: 32px;
min-height: 20px;
flex-direction: row;
flex-grow: 1;
flex-shrink: 0;
margin: 1px 0 0 1px;
padding-right: 2px;
padding-left: 2px;
border-top-width: 0;
border-right-width: 0;
border-bottom-width: 0;
border-left-width: 0;
border-radius: 3px;
}
#PanelButton.toolbarVerticalMode {
width: 32px;
height: 22px;
max-width: 32px;
max-height: 22px;
min-width: 32px;
min-height: 22px;
flex-direction: row;
flex-grow: 1;
flex-shrink: 0;
margin: 0 0 1px;
border-top-width: 0;
border-right-width: 0;
border-bottom-width: 0;
border-left-width: 0;
border-radius: 3px;
}
#ActionColor {
margin-left: 1px;
margin-right: 1px;
}
#ActionColor.toolbarMode {
height: 16px;
max-height: 16px;
min-height: 16px;
width: 4px;
max-width: 4px;
min-width: 4px;
background-position-x: left 0;
}
#ActionIcon {
margin-left: 1px;
margin-right: 1px;
width: 16px;
height: 16px;
min-height: 16px;
min-width: 16px;
max-width: 16px;
max-height: 16px;
}
#ActionIcon.toolbarMode {
width: 16px;
height: 16px;
max-width: 16px;
max-height: 16px;
min-height: 16px;
min-width: 16px;
background-position-x: center;
}
#ActionName {
margin-left: 1px;
margin-right: 1px;
}
#ActionName.toolbarMode {
display: none;
}
#PanelButton.panelMode {
flex-direction: row;
min-width: 30px;
}
.actionButton {
flex-shrink: 1;
flex-grow: 1;
flex-direction: row;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d2274e209af2ae458928061ab0671a0
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2bf443dd612131948a70af1b157c73ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<Style src="project://database/Packages/com.unity.probuilder/Editor/Overlays/StyleSheets/ActionOverlay.uss?fileID=7433441132597879392&amp;guid=4d2274e209af2ae458928061ab0671a0&amp;type=3#ActionOverlay"/>
<ui:VisualElement name="Root" class="actionButton">
<ui:Button text="&#10;" name="PanelButton" style="align-items: center; flex-direction: row; background-color: rgb(69, 69, 69); flex-shrink: 0; flex-grow: 1;">
<ui:VisualElement name="ActionColor" style="flex-grow: 1; width: 4px; max-width: 4px; min-width: 4px; background-color: rgb(200, 0, 0); flex-shrink: 0; height: 22px; margin-left: 2px;"/>
<ui:Image name="ActionIcon"/>
<ui:Label text="Label" name="ActionName" style="color: rgb(228, 228, 228);"/>
</ui:Button>
</ui:VisualElement>
</ui:UXML>
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 7ca30506f78e39b4c92a9b2e04fd3b1b
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}