// #define GENERATE_DESATURATED_ICONS
#if UNITY_2019_1_OR_NEWER
#define SHORTCUT_MANAGER
#endif
using UnityEngine;
using UnityEngine.ProBuilder;
namespace UnityEditor.ProBuilder
{
///
/// Base class from which any action that is represented in the ProBuilder toolbar inherits.
///
public abstract class MenuAction
{
///
/// A flag indicating the state of a menu action. This determines whether the menu item is visible, and if visible, enabled.
///
[System.Flags]
public enum MenuActionState
{
///
/// The button is not visible in the toolbar.
///
Hidden = 0x0,
///
/// The button is visible in the toolbar.
///
Visible = 0x1,
///
/// The button (and by proxy, the action it performs) are valid given the current selection.
///
Enabled = 0x2,
///
/// Button and action are both visible in the toolbar and valid given the current selection.
///
VisibleAndEnabled = 0x3
};
///
/// Path to the ProBuilder menu category.
///
///
/// Use this where you wish to add a top level menu item.
///
internal const string probuilderMenuPath = "Tools/ProBuilder/";
///
/// The unicode character for the control key symbol on Windows, or command key on macOS.
///
internal const char keyCommandSuper = PreferenceKeys.CMD_SUPER;
///
/// The unicode character for the shift key symbol.
///
internal const char keyCommandShift = PreferenceKeys.CMD_SHIFT;
///
/// The unicode character for the option key symbol on macOS.
///
///
internal const char keyCommandOption = PreferenceKeys.CMD_OPTION;
///
/// The unicode character for the alt key symbol on Windows.
///
internal const char keyCommandAlt = PreferenceKeys.CMD_ALT;
///
/// The unicode character for the delete key symbol.
///
internal const char keyCommandDelete = PreferenceKeys.CMD_DELETE;
static readonly GUIContent AltButtonContent = new GUIContent("+", "");
static readonly Vector2 AltButtonSize = new Vector2(21, 0);
Vector2 m_LastCalculatedSize = Vector2.zero;
protected MenuAction()
{
iconMode = ProBuilderEditor.s_IsIconGui;
}
///
/// Compare two menu items precedence by their category and priority modifier.
///
///
///
///
internal static int CompareActionsByGroupAndPriority(MenuAction left, MenuAction right)
{
if (left == null)
{
if (right == null)
return 0;
else
return -1;
}
else
{
if (right == null)
{
return 1;
}
else
{
int l = (int)left.group, r = (int)right.group;
if (l < r)
return -1;
else if (l > r)
return 1;
else
{
int lp = left.toolbarPriority < 0 ? int.MaxValue : left.toolbarPriority,
rp = right.toolbarPriority < 0 ? int.MaxValue : right.toolbarPriority;
return lp.CompareTo(rp);
}
}
}
}
Texture2D m_DesaturatedIcon = null;
///
/// By default this function will look for an image named `${icon}_disabled`. If your disabled icon is somewhere else override this function.
///
protected virtual Texture2D disabledIcon
{
get
{
if (m_DesaturatedIcon == null)
{
if (icon == null)
return null;
m_DesaturatedIcon = IconUtility.GetIcon(string.Format("Toolbar/{0}_disabled", icon.name));
#if GENERATE_DESATURATED_ICONS
if (!m_DesaturatedIcon)
m_DesaturatedIcon = ProBuilder2.EditorCommon.DebugUtilities.pb_GenerateDesaturatedImage.CreateDesaturedImage(icon);
#endif
}
return m_DesaturatedIcon;
}
}
///
/// What category this action belongs in.
///
public abstract ToolbarGroup group { get; }
///
/// Optional value influences where in the toolbar this menu item will be placed.
///
/// 0 is first, 1 is second, -1 is no preference.
///
///
public virtual int toolbarPriority { get { return -1; } }
///
/// The icon to be displayed for this action.
///
///
/// Not used when toolbar is in text mode.
///
public abstract Texture2D icon { get; }
///
/// The contents to display for this menu action's tooltip.
///
public abstract TooltipContent tooltip { get; }
///
/// Optional override for the action title displayed in the toolbar button.
///
///
/// If unimplemented the tooltip title is used.
///
public virtual string menuTitle { get { return tooltip.title; } }
///
/// True if this class should have an entry built into the hardware menu. This is not implemented for custom actions.
///
protected virtual bool hasFileMenuEntry { get { return true; } }
///
/// Is the current mode and selection valid for this action?
///
/// A flag indicating both the visibility and enabled state for an action.
public MenuActionState menuActionState
{
get
{
if (hidden)
return MenuActionState.Hidden;
if (enabled)
return MenuActionState.VisibleAndEnabled;
return MenuActionState.Visible;
}
}
///
/// In which SelectMode states is this action applicable. Drives the `virtual bool hidden { get; }` property unless overridden.
///
public virtual SelectMode validSelectModes
{
get { return SelectMode.Any; }
}
///
/// A check for whether or not the action is valid given the current selection.
///
///
/// True if this action is valid with current selection and mode.
public virtual bool enabled
{
get
{
return ProBuilderEditor.instance != null
&& ProBuilderEditor.selectMode.ContainsFlag(validSelectModes)
&& !ProBuilderEditor.selectMode.ContainsFlag(SelectMode.InputTool);
}
}
///
/// Is this action visible in the ProBuilder toolbar?
///
/// This returns false by default.
///
/// True if this action should be shown in the toolbar with the current mode and settings, false otherwise.
public virtual bool hidden
{
get { return !ProBuilderEditor.selectMode.ContainsFlag(validSelectModes); }
}
///
/// Get a flag indicating the visibility and enabled state of an extra options menu modifier for this action.
///
/// A flag specifying whether an options icon should be displayed for this action button. If your action implements some etra options, you must also implement OnSettingsGUI.
protected virtual MenuActionState optionsMenuState
{
get { return MenuActionState.Hidden; }
}
///
/// Perform whatever action this menu item is supposed to do. You are resposible for implementing Undo.
///
/// A new ActionResult with a summary of the state of the action's success.
public abstract ActionResult DoAction();
protected virtual void DoAlternateAction()
{
MenuOption.Show(OnSettingsGUI, OnSettingsEnable, OnSettingsDisable);
}
///
/// Implement the extra settings GUI for your action in this method.
///
protected virtual void OnSettingsGUI() {}
///
/// Called when the settings window is opened.
///
protected virtual void OnSettingsEnable() {}
///
/// Called when the settings window is closed.
///
protected virtual void OnSettingsDisable() {}
protected bool iconMode { get; set; }
///
/// Draw a menu button. Returns true if the button is active and settings are enabled, false if settings are not enabled.
///
///
///
///
///
///
internal bool DoButton(bool isHorizontal, bool showOptions, ref Rect optionsRect, params GUILayoutOption[] layoutOptions)
{
bool wasEnabled = GUI.enabled;
bool buttonEnabled = (menuActionState & MenuActionState.Enabled) == MenuActionState.Enabled;
GUI.enabled = buttonEnabled;
GUI.backgroundColor = Color.white;
if (iconMode)
{
if (GUILayout.Button(buttonEnabled || !disabledIcon ? icon : disabledIcon, ToolbarGroupUtility.GetStyle(group, isHorizontal), layoutOptions))
{
if (showOptions && (optionsMenuState & MenuActionState.VisibleAndEnabled) == MenuActionState.VisibleAndEnabled)
{
DoAlternateAction();
}
else
{
ActionResult result = DoAction();
EditorUtility.ShowNotification(result.notification);
}
}
if ((optionsMenuState & MenuActionState.VisibleAndEnabled) == MenuActionState.VisibleAndEnabled)
{
Rect r = GUILayoutUtility.GetLastRect();
r.x = r.x + r.width - 16;
r.y += 0;
r.width = 14;
r.height = 14;
GUI.Label(r, IconUtility.GetIcon("Toolbar/Options", IconSkin.Pro), GUIStyle.none);
optionsRect = r;
GUI.enabled = wasEnabled;
return buttonEnabled;
}
else
{
GUI.enabled = wasEnabled;
return false;
}
}
else
{
GUI.backgroundColor = ToolbarGroupUtility.GetColor(group);
// in text mode always use the vertical layout.
isHorizontal = false;
GUILayout.BeginHorizontal(MenuActionStyles.rowStyleVertical, layoutOptions);
if (GUILayout.Button(menuTitle, MenuActionStyles.buttonStyleVertical))
{
ActionResult res = DoAction();
EditorUtility.ShowNotification(res.notification);
}
MenuActionState altState = optionsMenuState;
if ((altState & MenuActionState.Visible) == MenuActionState.Visible)
{
GUI.enabled = GUI.enabled && (altState & MenuActionState.Enabled) == MenuActionState.Enabled;
if (DoAltButton(GUILayout.MaxWidth(21), GUILayout.MaxHeight(16)))
DoAlternateAction();
}
GUILayout.EndHorizontal();
GUI.backgroundColor = Color.white;
GUI.enabled = wasEnabled;
return false;
}
}
bool DoAltButton(params GUILayoutOption[] options)
{
return GUILayout.Button(AltButtonContent, MenuActionStyles.altButtonStyle, options);
}
///
/// Get the rendered width of this GUI item.
///
///
///
internal Vector2 GetSize(bool isHorizontal)
{
if (iconMode)
{
m_LastCalculatedSize = ToolbarGroupUtility.GetStyle(ToolbarGroup.Object, isHorizontal).CalcSize(UI.EditorGUIUtility.TempContent(null, null, icon));
}
else
{
// in text mode always use the vertical layout.
isHorizontal = false;
m_LastCalculatedSize = MenuActionStyles.buttonStyleVertical.CalcSize(UI.EditorGUIUtility.TempContent(menuTitle)) + AltButtonSize;
}
return m_LastCalculatedSize;
}
}
}