This commit is contained in:
陈思海 2025-08-01 19:32:29 +08:00
parent f432ee445b
commit 19135d607a
170 changed files with 4996 additions and 369 deletions

View File

@ -22,6 +22,16 @@ public static class AppBuildHelper
Debug.Log("Generate AppBuilderSetting.bytes");
PlayerSettings.fullScreenMode = appBuildParameter.FullScreenMode;
if (appBuildParameter.FullScreenMode == FullScreenMode.Windowed)
{
PlayerSettings.defaultScreenWidth = appBuildParameter.WindowedScreenSize.x;
PlayerSettings.defaultScreenHeight = appBuildParameter.WindowedScreenSize.y;
}
if (!string.IsNullOrEmpty(appBuildParameter.Version))
{
}
try
{

View File

@ -1,6 +1,7 @@
using System;
using AlicizaX;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
using YooAsset;
using YooAsset.Editor;
@ -16,6 +17,21 @@ public class AppBuildParameter
public string FileName;
public Language Language;
public string[] Scenes;
/// <summary>
/// 版本
/// </summary>
public string Version;
/// <summary>
/// 是否全屏
/// </summary>
public FullScreenMode FullScreenMode;
/// <summary>
/// 窗口化大小
/// </summary>
public Vector2Int WindowedScreenSize;
}

View File

@ -9,7 +9,7 @@ using System.IO;
public class EditorIcons : EditorWindow
{
[MenuItem("Tools/Editor Icons %e", priority = -1001)]
[MenuItem("Tools/EditorExtension/Editor Icons %e", priority = -1001)]
public static void EditorIconsOpen()
{
#if UNITY_2018
@ -236,7 +236,8 @@ public class EditorIcons : EditorWindow
List<GUIContent> iconList;
if (doSearch) iconList = iconContentListAll.Where(x => x.tooltip.ToLower()
if (doSearch)
iconList = iconContentListAll.Where(x => x.tooltip.ToLower()
.Contains(search.ToLower())).ToList();
else iconList = viewBigIcons ? iconContentListBig : iconContentListSmall;
@ -321,7 +322,6 @@ public class EditorIcons : EditorWindow
{
iconSelected = null;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 93fb9d2834752b649891dba19cba5169
guid: fb7e9c820d4ba2b4d836ffc849b55067
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 93fb9d2834752b649891dba19cba5169
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,69 @@
using System;
using AlicizaX.Localization.Runtime;
using AlicizaX;
using Paps.UnityToolbarExtenderUIToolkit;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
[MainToolbarElement("LocalizationDropdownField", alignment: ToolbarAlign.Right, order: 0)]
public class LocalizationDropdownField : IMGUIContainer
{
private static GUIContent appConfigBtContent;
private static string[] _languageTypeNames;
public void InitializeElement()
{
var nameArray = Enum.GetNames(typeof(Language));
_languageTypeNames = new string[nameArray.Length - 1];
for (int i = 1; i < nameArray.Length; i++)
{
var name = nameArray[i];
_languageTypeNames[i - 1] = name;
}
appConfigBtContent =
EditorGUIUtility.TrTextContentWithIcon("", "",
"Settings");
onGUIHandler = MyGUIMethod;
}
private void MyGUIMethod()
{
GUILayout.BeginHorizontal();
string title = _languageTypeNames[GetPrefsIndex()];
appConfigBtContent.text = title;
if (EditorGUILayout.DropdownButton(appConfigBtContent, FocusType.Passive, EditorStyles.toolbarPopup, GUILayout.MaxWidth(120)))
{
DrawEditorToolDropdownMenus();
}
GUILayout.Space(5);
GUILayout.EndHorizontal();
}
static void DrawEditorToolDropdownMenus()
{
int index = GetPrefsIndex();
GenericMenu popMenu = new GenericMenu();
for (int i = 0; i < _languageTypeNames.Length; i++)
{
var selected = index == i;
var toolAttr = _languageTypeNames[i];
popMenu.AddItem(new GUIContent(toolAttr), selected, menuIdx => { ClickToolsSubmenu((int)menuIdx); }, i);
}
popMenu.ShowAsContext();
}
static void ClickToolsSubmenu(int menuIdx)
{
EditorPrefs.SetInt(LocalizationComponent.PrefsKey, menuIdx + 1);
}
static int GetPrefsIndex()
{
return EditorPrefs.GetInt(LocalizationComponent.PrefsKey, 1) - 1;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2295b86f78274a84a63dc65ab4a69cb1
timeCreated: 1742364662

View File

@ -23,9 +23,7 @@ namespace AlicizaX.Editor.Extension
public void InitializeElement()
{
appConfigBtContent =
EditorGUIUtility.TrTextContentWithIcon("Res:", "配置App运行时所需DataTable/Config/Procedure",
"Settings");
appConfigBtContent = EditorGUIUtility.TrTextContentWithIcon("Res:", "配置App运行时资源模式", "Settings");
onGUIHandler = MyGUIMethod;
}

View File

@ -0,0 +1,40 @@
using System;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class ByAttributeMainToolbarElementRepository : IMainToolbarElementRepository
{
public MainToolbarElement[] GetAll()
{
return TypeCache.GetTypesWithAttribute<MainToolbarElementAttribute>()
.Where(type => IsValidVisualElementType(type))
.Select(type => GetMainToolbarElementFromType(type))
.ToArray();
}
private MainToolbarElement GetMainToolbarElementFromType(Type type)
{
var elementInstance = (VisualElement)Activator.CreateInstance(type);
var attribute = type.GetCustomAttribute<MainToolbarElementAttribute>();
if (string.IsNullOrEmpty(elementInstance.name))
elementInstance.name = elementInstance.GetType().Name;
return new MainToolbarElement(attribute.Id, elementInstance, attribute.Alignment,
attribute.Order, attribute.UseRecommendedStyles);
}
private bool IsValidVisualElementType(Type type)
{
var visualElementType = typeof(VisualElement);
return visualElementType != type &&
visualElementType.IsAssignableFrom(type) &&
!type.IsAbstract;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e664729ffc0507499365cb1b3393b91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e5d6770a557fcef47bfab9c1562f972c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,207 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
public class MainToolbarControlPanelWindow : EditorWindow
{
private const string NATIVE_ELEMENTS_CONTAINER_NAME = "UnityNativeElementsContainer";
private const string NATIVE_ELEMENTS_FOLDOUT_TEXT = "Unity Native Elements";
private const string SINGLE_ELEMENTS_CONTAINER_NAME = "SingleElementsContainer";
private const string SINGLE_ELEMENTS_FOLDOUT_TEXT = "Single Elements";
private const string GROUP_ELEMENTS_CONTAINER_NAME = "GroupElementsContainer";
private const string GROUP_ELEMENTS_FOLDOUT_TEXT = "Groups";
private const float MAIN_CONTAINER_PADDING_TOP = 5;
private OrganizationalFoldableContainer _nativeElementsContainer;
private OrganizationalFoldableContainer _singleElementsContainer;
private OrganizationalFoldableContainer _groupElementsContainer;
private MainToolbarElementController[] _controllers;
private Button _resetOverridesButton;
private VisualElement _noElementsMessageElement;
private VisualElement _windowContainer;
public static void OpenWindow()
{
var window = GetWindow<MainToolbarControlPanelWindow>();
window.titleContent = new GUIContent("Main Toolbar Control Panel");
}
private void CreateGUI()
{
MainToolbarAutomaticExtender.OnAddedCustomContainersToToolbar += Refresh;
MainToolbarAutomaticExtender.OnRefresh += Refresh;
BuildFixedGUI();
SetDefaultView();
if (MainToolbar.IsAvailable)
Refresh();
}
private void OnDestroy()
{
MainToolbarAutomaticExtender.OnAddedCustomContainersToToolbar -= Refresh;
MainToolbarAutomaticExtender.OnRefresh -= Refresh;
}
private void Refresh()
{
BuildDynamicGUI();
SetCorrespondingView();
}
private void BuildFixedGUI()
{
_windowContainer = GetContainer();
_resetOverridesButton = new Button(GlobalActions.ResetOverridesIfUserAccepts);
_resetOverridesButton.text = "Reset Overrides";
_singleElementsContainer = new OrganizationalFoldableContainer(
SINGLE_ELEMENTS_CONTAINER_NAME, SINGLE_ELEMENTS_FOLDOUT_TEXT);
_groupElementsContainer = new OrganizationalFoldableContainer(
GROUP_ELEMENTS_CONTAINER_NAME, GROUP_ELEMENTS_FOLDOUT_TEXT);
_nativeElementsContainer = new OrganizationalFoldableContainer(
NATIVE_ELEMENTS_CONTAINER_NAME, NATIVE_ELEMENTS_FOLDOUT_TEXT);
_noElementsMessageElement = CreateNoElementsMessageElement();
rootVisualElement.Add(_resetOverridesButton);
rootVisualElement.Add(_windowContainer);
}
private void SetDefaultView()
{
_windowContainer.Add(_noElementsMessageElement);
}
private void SetCorrespondingView()
{
if(_controllers.Length > 0)
{
if(_windowContainer.Contains(_noElementsMessageElement))
_windowContainer.Remove(_noElementsMessageElement);
if (!_windowContainer.Contains(_singleElementsContainer))
{
_windowContainer.Add(_singleElementsContainer);
_windowContainer.Add(_groupElementsContainer);
_windowContainer.Add(_nativeElementsContainer);
}
}
else
{
if (_windowContainer.Contains(_singleElementsContainer))
{
_windowContainer.Remove(_singleElementsContainer);
_windowContainer.Remove(_groupElementsContainer);
_windowContainer.Remove(_nativeElementsContainer);
}
if (!_windowContainer.Contains(_noElementsMessageElement))
_windowContainer.Add(_noElementsMessageElement);
}
}
private void BuildDynamicGUI()
{
_controllers = CreateControllers();
var controllersOfNativeElements = _controllers
.Where(controller => controller.HoldsANativeElement);
var controllersOfGroups = _controllers
.Where(controller => controller.HoldsAGroup);
var controllersOfSingleElements = _controllers
.Where(controller => !controller.HoldsAGroup && !controller.HoldsANativeElement)
.Where(controller => SingleControllerIsNotInsideAGroupController(controller, controllersOfGroups));
_singleElementsContainer.SetControllers(controllersOfSingleElements);
_groupElementsContainer.SetControllers(controllersOfGroups);
_nativeElementsContainer.SetControllers(controllersOfNativeElements);
}
private static bool SingleControllerIsNotInsideAGroupController(MainToolbarElementController controller, IEnumerable<MainToolbarElementController> controllersOfGroups)
{
foreach (var groupController in controllersOfGroups)
if (groupController.ContainsSubController(controller.Id))
return false;
return true;
}
private VisualElement GetContainer()
{
var scrollView = new ScrollView(ScrollViewMode.Vertical);
scrollView.style.paddingTop = MAIN_CONTAINER_PADDING_TOP;
return scrollView;
}
private MainToolbarElementController[] CreateControllers()
{
return GetOverridableElements()
.OrderBy(overridableElement => overridableElement.Id)
.Select(overridableElement => new MainToolbarElementController(
overridableElement,
ServicesAndRepositories.MainToolbarElementOverridesRepository,
GetSubElementsIfAny(overridableElement))
)
.ToArray();
}
private OverridableElement[] GetSubElementsIfAny(OverridableElement overridableElement)
{
if(overridableElement.VisualElement is GroupElement)
{
return MainToolbarAutomaticExtender.GetElementsOfGroup(overridableElement.Id)
.Select(el => new OverridableElement(
el.Id,
el.VisualElement,
false)
)
.ToArray();
}
return new OverridableElement[0];
}
private OverridableElement[] GetOverridableElements()
{
var nativeElements = MainToolbarAutomaticExtender.NativeElements
.Select(nativeElement => new OverridableElement(
nativeElement.Id,
nativeElement.VisualElement,
true
)
);
var customElements = MainToolbarAutomaticExtender.CustomMainToolbarElements
.Concat(MainToolbarAutomaticExtender.GroupElements)
.Select(mainToolbarElement => new OverridableElement(
mainToolbarElement.Id,
mainToolbarElement.VisualElement,
false
)
);
return nativeElements.Concat(customElements)
.ToArray();
}
private VisualElement CreateNoElementsMessageElement()
{
return new HelpBox("No main toolbar elements were found. To use the automatic toolbar extender define a main toolbar element. See the docs.", HelpBoxMessageType.Warning);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fef14479cdf8f5a41bb3b7f4b442484e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,187 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class MainToolbarElementController : VisualElement
{
private const string FOLDOUT_STATE_SAVE_KEY_BASE = "main-toolbar-element-controller:foldout-state:";
private const float LEFT_PADDING_SINGLE = 19;
private const float RIGHT_PADDING = 10;
private readonly IMainToolbarElementOverrideRepository _overridesRepository;
private Label _label;
private Button _button;
private Image _buttonIconImage;
private Foldout _foldout;
private StyleColor _defaultButtonColor;
private List<MainToolbarElementController> _subControllers = new List<MainToolbarElementController>();
public string Id { get; }
public VisualElement ControlledVisualElement { get; }
public bool HoldsAGroup => _foldout != null;
public bool HoldsANativeElement { get; private set; }
public MainToolbarElementController(OverridableElement overridableElement,
IMainToolbarElementOverrideRepository overridesRepository, params OverridableElement[] subElements)
{
Id = overridableElement.Id;
ControlledVisualElement = overridableElement.VisualElement;
HoldsANativeElement = overridableElement.IsNative;
name = Id + "-Controller";
_overridesRepository = overridesRepository;
_buttonIconImage = new Image();
style.flexDirection = FlexDirection.Row;
style.justifyContent = Justify.SpaceBetween;
style.paddingRight = RIGHT_PADDING;
_label = CreateLabel();
_button = CreateButton();
_defaultButtonColor = _button.style.backgroundColor;
UpdateButtonStatus(VisibleValueOrDefault());
BuildAsGroupOrSingle(subElements);
}
private string GetFullFoldoutStateSaveKey() => FOLDOUT_STATE_SAVE_KEY_BASE + Id;
private void SaveFoldoutState(ChangeEvent<bool> eventArgs)
{
UserSettingsPrefs.SetBool(GetFullFoldoutStateSaveKey(), eventArgs.newValue);
}
private bool GetSavedFoldoutState()
{
return UserSettingsPrefs.GetBool(GetFullFoldoutStateSaveKey(), false);
}
public bool ContainsSubController(string id)
{
return _subControllers.Any(controller => controller.Id == id);
}
private void BuildAsGroupOrSingle(OverridableElement[] subElements)
{
if (subElements.Length > 0)
BuildFoldout(subElements);
else
BuildSingleElement();
}
private void BuildSingleElement()
{
style.paddingLeft = LEFT_PADDING_SINGLE;
Add(_label);
Add(_button);
}
private void BuildFoldout(OverridableElement[] subElements)
{
_foldout = new Foldout() { text = _label.text };
foreach (var overridable in subElements)
{
var subController = new MainToolbarElementController(overridable, _overridesRepository);
_subControllers.Add(subController);
_foldout.Add(subController);
}
_foldout.value = GetSavedFoldoutState();
_foldout.RegisterCallback<ChangeEvent<bool>>(SaveFoldoutState);
_foldout.style.flexGrow = 1;
Add(_foldout);
Add(_button);
}
private bool VisibleValueOrDefault()
{
var possibleOverride = _overridesRepository.Get(Id);
if (possibleOverride == null)
return CurrentDisplayValueAsBool();
var overrideValue = possibleOverride.Value;
return overrideValue.Visible;
}
private Label CreateLabel()
{
var label = new Label(Id);
label.style.alignSelf = Align.Center;
return label;
}
private Button CreateButton()
{
var button = new Button(ChangeVisibilityValue);
button.Add(_buttonIconImage);
button.tooltip = "Change the visibility of element with id " + Id;
button.style.alignSelf = Align.FlexStart;
return button;
}
private Texture IconByVisibilityValue(bool visible)
{
if (visible)
return Icons.VisibilityOnOverrideIcon;
return Icons.VisibilityOffOverrideIcon;
}
private void ChangeVisibilityValue()
{
var currentOverrideValue = _overridesRepository.Get(Id);
bool newValue;
if (currentOverrideValue == null)
newValue = !CurrentDisplayValueAsBool();
else
newValue = !currentOverrideValue.Value.Visible;
_overridesRepository.Save(new MainToolbarElementOverride(Id, newValue));
UpdateButtonStatus(newValue);
MainToolbarAutomaticExtender.Refresh();
}
private bool CurrentDisplayValueAsBool()
{
if (ControlledVisualElement.style.display == DisplayStyle.None)
return false;
else
return true;
}
private void UpdateButtonStatus(bool visible)
{
_buttonIconImage.image = IconByVisibilityValue(visible);
_button.style.backgroundColor = GetButtonColor(visible);
}
private StyleColor GetButtonColor(bool visible)
{
if (visible)
return _defaultButtonColor;
else
return new StyleColor(Color.black);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae80166256b03484a9bfc1ad9c4cc0dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class OrganizationalFoldableContainer : Box
{
private static readonly Color ORGANIZATIONAL_FOLDABLE_CONTAINER_BORDER_COLOR = new Color(153f / 255f, 153f / 255f, 153f / 255f);
private const string FOLDOUT_STATE_SAVE_KEY_BASE = "organizational-foldable-container:foldout-state:";
private Foldout _foldout;
private string _id;
public OrganizationalFoldableContainer(string containerId, string foldoutText)
{
_id = containerId;
name = containerId;
style.borderTopColor = ORGANIZATIONAL_FOLDABLE_CONTAINER_BORDER_COLOR;
style.borderTopWidth = 1;
_foldout = new Foldout() { text = foldoutText };
_foldout.value = GetSavedFoldoutState();
_foldout.RegisterCallback<ChangeEvent<bool>>(SaveFoldoutState);
Add(_foldout);
}
private string GetFullFoldoutStateSaveKey() => FOLDOUT_STATE_SAVE_KEY_BASE + _id;
private void SaveFoldoutState(ChangeEvent<bool> eventArgs)
{
UserSettingsPrefs.SetBool(GetFullFoldoutStateSaveKey(), eventArgs.newValue);
}
private bool GetSavedFoldoutState()
{
return UserSettingsPrefs.GetBool(GetFullFoldoutStateSaveKey(), false);
}
public void SetControllers(IEnumerable<MainToolbarElementController> controllers)
{
_foldout.Clear();
foreach (var controller in controllers)
{
_foldout.Add(controller);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9ac00cb9059df7e43b1c2ed433ce61cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal readonly struct OverridableElement
{
public string Id { get; }
public VisualElement VisualElement { get; }
public bool IsNative { get; }
public OverridableElement(string id, VisualElement visualElement, bool isNative)
{
Id = id;
VisualElement = visualElement;
IsNative = isNative;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 542f1fb68120090489ba1613162eb17b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f0a074f24b9c98d488341e6258c0c0b0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,17 @@
using UnityEditor;
using UnityEngine;
namespace Paps.UnityToolbarExtenderUIToolkit
{
public static class EditorWindowExtensions
{
public static void ShowAsDropdownForMainToolbar(this EditorWindow window, Rect activatorRect, Vector2 size)
{
window.ShowAsDropDown(activatorRect, size);
var rect = GUIUtility.GUIToScreenRect(activatorRect);
rect.y += activatorRect.size.y;
window.position = new Rect(rect.position, window.position.size);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3333a000c60ebce4aa038ee0945d0072
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1a83afb20ef0f5f40b22a93ff124fe1f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using System.Linq;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal readonly struct GroupDefinition
{
public string GroupId { get; }
public string GroupName { get; }
public ToolbarAlign Alignment { get; }
public int Order { get; }
public string[] ToolbarElementsIds { get; }
public GroupDefinition(string groupId, string groupName, ToolbarAlign alignment, int order, string[] toolbarElementsIds)
{
GroupId = groupId;
GroupName = string.IsNullOrEmpty(groupName) ? groupId : groupName;
Alignment = alignment;
Order = order;
ToolbarElementsIds = toolbarElementsIds;
}
public bool AreEquals(GroupDefinition other)
{
return GroupId == other.GroupId &&
GroupName == other.GroupName &&
Alignment == other.Alignment &&
Order == other.Order &&
AreEquals(ToolbarElementsIds, other.ToolbarElementsIds);
}
private bool AreEquals(string[] types, string[] types2)
{
if(types.Length != types2.Length)
return false;
return types.All(typeName => types2.Contains(typeName));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e3f2e82a96ef3246860b2861fa19b15
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,92 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class GroupDropdownWindowPopup : EditorWindow
{
private VisualElement[] _groupElements;
private ScrollView _scrollView;
public void Initialize(VisualElement[] groupElements)
{
_groupElements = groupElements;
hideFlags = HideFlags.DontSave;
}
private void OnEnable()
{
AssemblyReloadEvents.beforeAssemblyReload += Close;
}
private void OnDisable()
{
AssemblyReloadEvents.beforeAssemblyReload -= Close;
}
private void CreateGUI()
{
_scrollView = new ScrollView(ScrollViewMode.Vertical);
_scrollView.contentContainer.style.alignContent = Align.Center;
_scrollView.contentContainer.style.alignItems = Align.Center;
_scrollView.horizontalScrollerVisibility = ScrollerVisibility.Hidden;
foreach (var groupElement in _groupElements)
{
var container = CreateGroupElementContainer();
container.Add(groupElement);
groupElement.style.flexWrap = Wrap.Wrap;
container.style.display = groupElement.style.display;
_scrollView.Add(container);
}
var baseContainer = CreateBaseContainer();
baseContainer.Add(_scrollView);
rootVisualElement.Add(baseContainer);
}
private VisualElement CreateBaseContainer()
{
return new VisualElement()
{
style =
{
flexGrow = 1,
borderBottomColor = Color.black,
borderTopColor = Color.black,
borderLeftColor = Color.black,
borderRightColor = Color.black,
borderBottomWidth = 2,
borderTopWidth = 2,
borderLeftWidth = 2,
borderRightWidth = 2,
}
};
}
private VisualElement CreateGroupElementContainer()
{
return new Box()
{
style =
{
alignContent = Align.Center,
alignItems = Align.Center,
flexDirection = FlexDirection.Row,
flexGrow = 1,
width = Length.Percent(100),
height = Length.Auto(),
justifyContent = Justify.Center,
borderTopColor = Color.black,
borderTopWidth = 1,
paddingTop = 3f,
paddingBottom = 3f,
paddingLeft = 8,
paddingRight = 8,
}
};
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8509c9dbf1627b74c9e521402a3de28b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using PopupWindow = UnityEditor.PopupWindow;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class GroupDropdownWindowPopupManager
{
private const string POPUP_WINDOW_CONTENT_FIELD_NAME = "m_WindowContent";
private static readonly string[] SPECIAL_EDITOR_WINDOW_TYPE_NAMES =
{
"UnityEditor.UIElements.Debugger.UIElementsDebugger",
"UnityEditor.UIElements.EditorMenuExtensions+ContextMenu",
"UnityEditor.ObjectSelector",
"UnityEditor.Search.SearchPickerWindow"
};
private static readonly string[] SPECIAL_POPUP_WINDOW_CONTENT_TYPE_NAMES =
{
"UnityEditor.UIElements.EditorGenericDropdownMenuWindowContent"
};
private static FieldInfo _popupWindowContentField;
private static Type[] _subWindowTypes = new Type[0];
private static Type[] _specialWindowTypes = new Type[0];
private static List<GroupDropdownWindowPopup> _windows = new List<GroupDropdownWindowPopup>();
static GroupDropdownWindowPopupManager()
{
_popupWindowContentField = typeof(PopupWindow)
.GetField(POPUP_WINDOW_CONTENT_FIELD_NAME,
BindingFlags.NonPublic | BindingFlags.Instance);
InitializeSpecialWindowTypes();
LoadSubWindowTypes();
EditorApplication.update += Update;
}
private static void LoadSubWindowTypes()
{
_subWindowTypes = TypeCache.GetTypesWithAttribute<GroupPopupSubWindowAttribute>()
.Where(type => IsValidSubWindowType(type))
.ToArray();
}
private static void InitializeSpecialWindowTypes()
{
var typeList = new List<Type>();
typeList.AddRange(TypeCache.GetTypesDerivedFrom<EditorWindow>()
.Where(type => SPECIAL_EDITOR_WINDOW_TYPE_NAMES.Contains(type.Name) || SPECIAL_EDITOR_WINDOW_TYPE_NAMES.Contains(type.FullName)));
typeList.AddRange(TypeCache.GetTypesDerivedFrom<PopupWindowContent>()
.Where(type => SPECIAL_POPUP_WINDOW_CONTENT_TYPE_NAMES.Contains(type.Name) || SPECIAL_POPUP_WINDOW_CONTENT_TYPE_NAMES.Contains(type.FullName)));
_specialWindowTypes = typeList.Where(t => t != null).ToArray();
}
private static bool IsValidSubWindowType(Type type)
{
return typeof(EditorWindow).IsAssignableFrom(type) ||
typeof(PopupWindowContent).IsAssignableFrom(type);
}
private static bool ContainsValidPopupWindowContent(EditorWindow window)
{
if(window is PopupWindow popupWindow)
{
var popupContent = _popupWindowContentField.GetValue(popupWindow) as PopupWindowContent;
var popupContentSpecificType = popupContent.GetType();
return _subWindowTypes.Contains(popupContentSpecificType) || _specialWindowTypes.Contains(popupContentSpecificType);
}
return false;
}
private static void Update()
{
if (_windows.Count == 0)
return;
if (EditorWindow.focusedWindow == null || !FocusedWindowIsValid())
CloseAll();
else if(_windows.Contains(EditorWindow.focusedWindow))
{
var focusedWindow = EditorWindow.focusedWindow;
var downMostWindow = _windows.Last();
while(downMostWindow != focusedWindow)
{
_windows.Remove(downMostWindow);
downMostWindow.Close();
downMostWindow = _windows.Last();
}
}
}
private static void CloseAll()
{
foreach(var window in _windows)
{
if(window != null)
window.Close();
}
_windows.Clear();
}
private static bool FocusedWindowIsValid()
{
return _windows.Contains(EditorWindow.focusedWindow) ||
_subWindowTypes.Contains(EditorWindow.focusedWindow.GetType()) ||
_specialWindowTypes.Contains(EditorWindow.focusedWindow.GetType()) ||
ContainsValidPopupWindowContent(EditorWindow.focusedWindow);
}
public static void Show(Rect activatorRect, VisualElement[] elements)
{
var window = ScriptableObject.CreateInstance<GroupDropdownWindowPopup>();
var rect = GUIUtility.GUIToScreenRect(activatorRect);
rect.y += activatorRect.size.y;
window.position = new Rect(rect.position, window.position.size);
_windows.Add(window);
window.Initialize(elements);
#if UNITY_EDITOR_WIN
window.ShowPopup();
#elif UNITY_EDITOR_OSX
window.ShowAsDropdownForMainToolbar(activatorRect, window.position.size);
#endif
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88333c3cc209cb143bc93891e1664b41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
using System.Linq;
using UnityEditor.Toolbars;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class GroupElement : EditorToolbarDropdown
{
private VisualElement[] _groupedElements;
public VisualElement[] GroupedElements => _groupedElements.ToArray();
public GroupElement(string name)
{
this.name = name;
text = name;
clicked += ShowDropdown;
}
public void Initialize(VisualElement[] groupedElements)
{
_groupedElements = groupedElements;
}
private void ShowDropdown()
{
GroupDropdownWindowPopupManager.Show(worldBound, _groupedElements);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c782e5c3d1d2bd7499b358c6aa5703cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,10 @@
using System;
namespace Paps.UnityToolbarExtenderUIToolkit
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class GroupPopupSubWindowAttribute : Attribute
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f6e542ea23caed94c92fab5ac0d0f1c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,7 @@
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal interface IGroupDefinitionRepository
{
public GroupDefinition[] GetAll();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15164a1a1815ea04c9ae72e65e0a9fe9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
using System;
using UnityEngine;
namespace Paps.UnityToolbarExtenderUIToolkit
{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class MainToolbarElementDropdownAttribute : PropertyAttribute
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ffb57bb326f1c1247b6e1341c5caecf3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,84 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
[CustomPropertyDrawer(typeof(MainToolbarElementDropdownAttribute))]
internal class MainToolbarElementDropdownDrawer : PropertyDrawer
{
private string _groupId;
private IEnumerable<string> _mainToolbarElementsIds;
private IEnumerable<string> _groupIds;
private IEnumerable<string> _allIds;
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
_groupId = property.serializedObject.FindProperty("_groupId").stringValue;
CacheAllIds();
var availableIds = GetAvailableIds();
var popupField = new PopupField<string>(
choices: availableIds,
0);
Restore(property, availableIds, popupField);
popupField.RegisterCallback<ChangeEvent<string>>(ev =>
{
property.stringValue = ev.newValue;
property.serializedObject.ApplyModifiedProperties();
ScriptableGroupDefinitionHelper.Refresh();
CacheAllIds();
var newAvailableIds = GetAvailableIds();
newAvailableIds.Add(property.stringValue);
popupField.choices = newAvailableIds.OrderBy(id => id).ToList();
});
return popupField;
}
private void Restore(SerializedProperty property, List<string> availableIds, PopupField<string> popupField)
{
if (string.IsNullOrEmpty(property.stringValue))
{
property.stringValue = popupField.value;
property.serializedObject.ApplyModifiedProperties();
}
else
{
if (_allIds.Contains(property.stringValue))
{
availableIds.Add(property.stringValue);
popupField.choices = availableIds.OrderBy(id => id).ToList();
popupField.SetValueWithoutNotify(property.stringValue);
}
else
popupField.SetValueWithoutNotify("");
}
}
private void CacheAllIds()
{
_mainToolbarElementsIds = MainToolbarAutomaticExtender.CustomMainToolbarElements.Select(el => el.Id);
_groupIds = ScriptableGroupDefinitionHelper.GetGroupIds();
_allIds = _mainToolbarElementsIds
.Concat(_groupIds);
}
private List<string> GetAvailableIds()
{
var unusedMainToolbarElementIds = ScriptableGroupDefinitionHelper.GetUnusedMainToolbarElementIds(_mainToolbarElementsIds);
var unusedGroupIds = ScriptableGroupDefinitionHelper.GetEligibleGroupChildsFor(_groupId);
return unusedMainToolbarElementIds
.Concat(unusedGroupIds)
.OrderBy(id => id)
.ToList();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cb51db686d5e79d4295bc1a510395e2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
using UnityEngine;
namespace Paps.UnityToolbarExtenderUIToolkit
{
[CreateAssetMenu(menuName = ToolInfo.EDITOR_MENU_BASE + "/Group Definition")]
public sealed class ScriptableGroupDefinition : ScriptableObject
{
[SerializeField]
[Tooltip("Id of group. It must be unique")]
private string _groupId;
[SerializeField]
[Tooltip("Text to display in group element dropdown")]
private string _groupName;
[SerializeField]
[Tooltip("Alignment when used as root element. Ignored inside other groups")]
private ToolbarAlign _alignment;
[Tooltip("Order when used as root element. Ignored inside other groups. Order of elements inside a group is determined by ToolbarElementsIds array elements order.")]
[SerializeField] private int _order;
[SerializeField]
[Tooltip("Elements ids of this group. Order of elements in array determines the order in which the elements will be displayed")]
[MainToolbarElementDropdown] private string[] _toolbarElementsIds;
public string GroupId => _groupId;
public string GroupName => _groupName;
public ToolbarAlign Alignment => _alignment;
public int Order => _order;
public string[] ToolbarElementsIds => _toolbarElementsIds == null ? new string[0] : _toolbarElementsIds;
private void OnValidate()
{
SetEqualValuesToEmptyInOrder();
}
private void SetEqualValuesToEmptyInOrder()
{
for (int i = 0; i < _toolbarElementsIds.Length; i++)
{
var value = _toolbarElementsIds[i];
for (int j = 0; j < _toolbarElementsIds.Length; j++)
{
if (i == j)
continue;
if (_toolbarElementsIds[j] == value)
{
_toolbarElementsIds[j] = "";
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6df7c06c2de586e41bb0c33bfd80766f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class ScriptableGroupDefinitionHelper
{
private static ScriptableGroupDefinition[] _projectGroupDefinitions;
static ScriptableGroupDefinitionHelper()
{
LoadProjectGroupDefinitions();
EditorApplication.projectChanged += OnProjectChange;
}
public static void Refresh()
{
LoadProjectGroupDefinitions();
}
private static void LoadProjectGroupDefinitions()
{
var paths = AssetDatabase.FindAssets("t:" + nameof(ScriptableGroupDefinition))
.Select(guid => AssetDatabase.GUIDToAssetPath(guid));
_projectGroupDefinitions = paths
.Select(path => AssetDatabase.LoadAssetAtPath<ScriptableGroupDefinition>(path))
.ToArray();
}
public static IEnumerable<string> GetUnusedMainToolbarElementIds(IEnumerable<string> allIds)
{
return allIds.Except(GetUsedMainToolbarElementIds());
}
private static IEnumerable<string> GetUsedMainToolbarElementIds()
{
return _projectGroupDefinitions
.SelectMany(groupDefinition => groupDefinition.ToolbarElementsIds);
}
public static IEnumerable<string> GetGroupIds()
{
return _projectGroupDefinitions.Select(g => g.GroupId);
}
public static IEnumerable<string> GetEligibleGroupChildsFor(string groupId)
{
var allUsedIds = _projectGroupDefinitions.SelectMany(g => g.ToolbarElementsIds);
return _projectGroupDefinitions.Select(g => g.GroupId)
.Where(id => !allUsedIds.Contains(id))
.Where(id => id != groupId);
}
private static void OnProjectChange()
{
Refresh();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12a0d38180307184e87497e8fbb28985
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using System.Linq;
using UnityEditor;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class ScriptableObjectGroupDefinitionRepository : IGroupDefinitionRepository
{
public GroupDefinition[] GetAll()
{
var paths = AssetDatabase.FindAssets("t:" + nameof(ScriptableGroupDefinition))
.Select(guid => AssetDatabase.GUIDToAssetPath(guid));
return paths
.Select(path => AssetDatabase.LoadAssetAtPath<ScriptableGroupDefinition>(path))
.Where(scriptableGroupDefinition => !string.IsNullOrEmpty(scriptableGroupDefinition.GroupId))
.GroupBy(scriptableGroupDefinition => scriptableGroupDefinition.GroupId)
.Select(scriptableGroupDefinition => scriptableGroupDefinition.First())
.Select(scriptableGroupDefinition => new GroupDefinition(
scriptableGroupDefinition.GroupId,
scriptableGroupDefinition.GroupName,
scriptableGroupDefinition.Alignment,
scriptableGroupDefinition.Order,
FilterIds(scriptableGroupDefinition)
)
)
.Where(m => m.ToolbarElementsIds.Length > 0)
.ToArray();
}
private string[] FilterIds(ScriptableGroupDefinition scriptableGroupDefinition)
{
return scriptableGroupDefinition.ToolbarElementsIds
.Where(id => !string.IsNullOrEmpty(id))
.Distinct()
.ToArray();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 225393fc4d1ad744c972f3a876dce3df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: efeeca875749f6d46905cc399ed76bae
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using System;
using UnityEditor;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class GlobalActions
{
public static void ResetOverrides()
{
ServicesAndRepositories.MainToolbarElementOverridesRepository.Clear();
MainToolbarAutomaticExtender.Refresh();
}
public static void ResetOverridesIfUserAccepts()
{
ShowDialog(
"Reset Overrides",
"You are about to reset all toolbar elements overrides.\nAre you sure you want to continue?",
"Reset",
"Cancel",
ResetOverrides
);
}
public static void ShowDialog(string title, string message, string okMessage,
string cancelMessage, Action onOk = null, Action onCancel = null)
{
if (EditorUtility.DisplayDialog(title, message, okMessage, cancelMessage))
onOk?.Invoke();
else
onCancel?.Invoke();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bfe995773a109214db8292f8ce74d342
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using System;
using UnityEditor;
using UnityEngine;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class Icons
{
private static readonly Lazy<Texture> VISIBILITY_ON_OVERRIDE_ICON_LAZY =
new Lazy<Texture>(() => EditorGUIUtility.IconContent("animationvisibilitytoggleon").image);
private static readonly Lazy<Texture> VISIBILITY_OFF_OVERRIDE_ICON_LAZY =
new Lazy<Texture>(() => EditorGUIUtility.IconContent("animationvisibilitytoggleoff").image);
public static Texture VisibilityOnOverrideIcon => VISIBILITY_ON_OVERRIDE_ICON_LAZY.Value;
public static Texture VisibilityOffOverrideIcon => VISIBILITY_OFF_OVERRIDE_ICON_LAZY.Value;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51ede7e0c6a32a74faa6298082ed29cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
using UnityEditor;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class MenuItems
{
[MenuItem(ToolInfo.EDITOR_MENU_BASE + "/Refresh Toolbar Extender", priority = 1)]
public static void Refresh()
{
MainToolbarAutomaticExtender.Refresh();
}
[MenuItem(ToolInfo.EDITOR_MENU_BASE + "/Main Toolbar Control Panel", priority = 12)]
public static void OpenControlPanel()
{
MainToolbarControlPanelWindow.OpenWindow();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a74255adbb601344cb6b565d617de6f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using System;
using System.Linq;
using UnityEditor;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class RefreshToolbarAutomaticExtenderOnSave : AssetModificationProcessor
{
private static string[] OnWillSaveAssets(string[] paths)
{
var groupDefinitionsAssetsPaths = AssetDatabase.FindAssets("t:" + nameof(ScriptableGroupDefinition))
.Select(guid => AssetDatabase.GUIDToAssetPath(guid))
.ToArray();
if (GroupDefinitionAssetIsBeingSaved(paths, groupDefinitionsAssetsPaths))
{
EditorApplication.update += RefreshOneTime;
}
return paths;
}
private static bool GroupDefinitionAssetIsBeingSaved(string[] savingAssetsPaths, string[] groupDefinitionsAssetsPaths)
{
return savingAssetsPaths.Any(path => groupDefinitionsAssetsPaths.Contains(path));
}
private static void RefreshOneTime()
{
EditorApplication.update -= RefreshOneTime;
Refresh();
}
private static void Refresh()
{
MainToolbarAutomaticExtender.Refresh();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e74405729947e434fb3760be36ad262e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class ServicesAndRepositories
{
public static IMainToolbarElementOverrideRepository MainToolbarElementOverridesRepository =
new UserSettingsFileMainToolbarElementOverrideRepository();
public static IGroupDefinitionRepository GroupDefinitionRepository =
new ScriptableObjectGroupDefinitionRepository();
public static IMainToolbarElementRepository MainToolbarElementRepository =
new ByAttributeMainToolbarElementRepository();
public static IValueSerializer ValueSerializer =
new UnitySerializationValueSerializer();
public static IMainToolbarElementVariableSerializer MainToolbarElementVariableSerializer =
new UnitySerializationMainToolbarElementVariableSerializer(ValueSerializer);
public static IMainToolbarElementVariableRepository MainToolbarElementVariableRepository =
new UserSettingsFileMainToolbarElementVariableRepository(MainToolbarElementVariableSerializer);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f95a2c4cbecd4f419ae142074dbb03f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class ToolInfo
{
public const string COMPANY_NAME = "Tools/EditorExtension/Toolbar";
public const string EDITOR_MENU_BASE = COMPANY_NAME + "/" ;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c363bf3b832d93c45b14669aa6ae573c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,120 @@
using System.Collections.Generic;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal static class UnityNativeElementsIds
{
// ------------------- ELEMENTS TYPES ---------------------
// LEFT
private const string ACCOUNT_DROPDOWN_TYPE_NAME = "AccountDropdown";
private const string CLOUD_BUTTON_TYPE_NAME = "CloudButton";
private const string VERSION_CONTROL_BUTTON_TYPE_NAME = "MainToolbarImguiContainer";
private const string STORE_BUTTON_TYPE_NAME = "StoreButton";
// RIGHT
private const string LAYOUT_DROPDOWN_TYPE_NAME = "LayoutDropdown";
private const string LAYERS_DROPDOWN_TYPE_NAME = "LayersDropdown";
private const string SEARCH_BUTTON_TYPE_NAME = "SearchButton";
private const string MODES_DROPDOWN_TYPE_NAME = "ModesDropdown";
private const string PREVIEW_PACKAGES_IN_USE_DROPDOWN_TYPE_NAME = "PreviewPackagesInUseDropdown";
private const string UNDO_BUTTON_TYPE_NAME = "UndoButton";
private const string MULTIPLAYER_ROLE_DROPDOWN_TYPE_NAME = "MultiplayerRoleDropdown";
// ------------------- ELEMENTS NAMES ---------------------
// LEFT
private const string TOOLBAR_PRODUCT_CAPTION_NAME = "ToolbarProductCaption";
private const string ACCOUNT_DROPDOWN_ELEMENT_NAME = "AccountDropdown";
private const string CLOUD_BUTTON_ELEMENT_NAME = "Cloud";
// CENTER
private const string PLAY_BUTTON_ELEMENT_NAME = "Play";
private const string PAUSE_BUTTON_ELEMENT_NAME = "Pause";
private const string FRAME_STEP_BUTTON_ELEMENT_NAME = "Step";
// RIGHT
private const string LAYOUT_DROPDOWN_ELEMENT_NAME = "LayoutDropdown";
private const string LAYERS_DROPDOWN_ELEMENT_NAME = "LayersDropdown";
private const string MODES_DROPDOWN_ELEMENT_NAME = "ModesDropdown";
private const string PREVIEW_PACKAGES_IN_USE_DROPDOWN_ELEMENT_NAME = "PreviewPackagesInUseDropdown";
private const string UNDO_BUTTON_ELEMENT_NAME = "History";
// ------------------- FIXED IDS ---------------------
// LEFT
public const string TOOLBAR_PRODUCT_CAPTION = "ToolbarProductCaption";
public const string ACCOUNT_DROPDOWN_ID = "AccountDropdown";
public const string CLOUD_BUTTON_ID = "CloudButton";
public const string VERSION_CONTROL_ID = "VersionControlButton";
public const string STORE_BUTTON_ID = "StoreButton";
// CENTER
public const string PLAY_BUTTON_ID = "PlayButton";
public const string PAUSE_BUTTON_ID = "PauseButton";
public const string FRAME_STEP_BUTTON_ID = "FrameStepButton";
// RIGHT
public const string LAYOUT_DROPDOWN_ID = "LayoutDropdown";
public const string LAYERS_DROPDOWN_ID = "LayersDropdown";
public const string SEARCH_BUTTON_ID = "SearchButton";
public const string MODES_DROPDOWN_ID = "ModesDropdown";
public const string PREVIEW_PACKAGES_IN_USE_DROPDOWN_ID = "PreviewPackagesInUseDropdown";
public const string UNDO_BUTTON_ID = "UndoButton";
public const string MULTIPLAYER_ROLE_DROPDOWN = "MultiplayerRoleDropdown";
private static readonly Dictionary<string, string> IDS_BY_TYPE = new Dictionary<string, string>()
{
// LEFT
{ ACCOUNT_DROPDOWN_TYPE_NAME, ACCOUNT_DROPDOWN_ID },
{ CLOUD_BUTTON_TYPE_NAME, CLOUD_BUTTON_ID },
{ VERSION_CONTROL_BUTTON_TYPE_NAME, VERSION_CONTROL_ID },
{ STORE_BUTTON_TYPE_NAME, STORE_BUTTON_ID },
// RIGHT
{ LAYOUT_DROPDOWN_TYPE_NAME, LAYOUT_DROPDOWN_ID },
{ LAYERS_DROPDOWN_TYPE_NAME, LAYERS_DROPDOWN_ID },
{ SEARCH_BUTTON_TYPE_NAME, SEARCH_BUTTON_ID },
{ MODES_DROPDOWN_TYPE_NAME, MODES_DROPDOWN_ID },
{ PREVIEW_PACKAGES_IN_USE_DROPDOWN_TYPE_NAME, PREVIEW_PACKAGES_IN_USE_DROPDOWN_ID },
{ UNDO_BUTTON_TYPE_NAME, UNDO_BUTTON_ID },
{ MULTIPLAYER_ROLE_DROPDOWN_TYPE_NAME, MULTIPLAYER_ROLE_DROPDOWN }
};
private static readonly Dictionary<string, string> IDS_BY_NAME = new Dictionary<string, string>()
{
// LEFT
{ TOOLBAR_PRODUCT_CAPTION_NAME, TOOLBAR_PRODUCT_CAPTION },
{ ACCOUNT_DROPDOWN_ELEMENT_NAME, ACCOUNT_DROPDOWN_ID },
{ CLOUD_BUTTON_ELEMENT_NAME, CLOUD_BUTTON_ID },
// CENTER
{ PLAY_BUTTON_ELEMENT_NAME, PLAY_BUTTON_ID },
{ PAUSE_BUTTON_ELEMENT_NAME, PAUSE_BUTTON_ID },
{ FRAME_STEP_BUTTON_ELEMENT_NAME, FRAME_STEP_BUTTON_ID },
// RIGHT
{ LAYOUT_DROPDOWN_ELEMENT_NAME, LAYOUT_DROPDOWN_ID },
{ LAYERS_DROPDOWN_ELEMENT_NAME, LAYERS_DROPDOWN_ID },
{ MODES_DROPDOWN_ELEMENT_NAME, MODES_DROPDOWN_ID },
{ PREVIEW_PACKAGES_IN_USE_DROPDOWN_ELEMENT_NAME, PREVIEW_PACKAGES_IN_USE_DROPDOWN_ID },
{ UNDO_BUTTON_ELEMENT_NAME, UNDO_BUTTON_ID },
};
public static string IdOf(VisualElement visualElement)
{
var typeName = visualElement.GetType().Name;
if(IDS_BY_TYPE.ContainsKey(typeName))
return IDS_BY_TYPE[typeName];
var elementName = visualElement.name;
if(IDS_BY_NAME.ContainsKey(elementName))
return IDS_BY_NAME[elementName];
return null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62370caf7a9d1274ba18d9556d83fd07
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.IO;
using Unity.Serialization.Json;
using UnityEngine;
namespace Paps.UnityToolbarExtenderUIToolkit
{
public static class UserSettingsPrefs
{
private static readonly string DIRECTORY = Path.Combine(Directory.GetParent(Application.dataPath).FullName, "UserSettings/", "unity-toolbar-extender-ui-toolkit", "user-settings-prefs");
private static readonly string FILE = Path.Combine(DIRECTORY, "user-settings-prefs.json");
private static Dictionary<string, object> _prefs;
private static Dictionary<string, object> Prefs
{
get
{
if (_prefs == null)
_prefs = Load();
return _prefs;
}
}
public static void SetInt(string key, int value)
{
Prefs[key] = value;
Save();
}
public static void SetFloat(string key, float value)
{
Prefs[key] = value;
Save();
}
public static void SetDouble(string key, double value)
{
Prefs[key] = value;
Save();
}
public static void SetBool(string key, bool value)
{
Prefs[key] = value;
Save();
}
public static void SetString(string key, string value)
{
Prefs[key] = value;
Save();
}
public static string GetString(string key, string defaultValue = null)
{
if (Prefs.ContainsKey(key))
return (string)Prefs[key];
return defaultValue;
}
public static bool GetBool(string key, bool defaultValue = false)
{
if (Prefs.ContainsKey(key))
return (bool)Prefs[key];
return defaultValue;
}
public static int GetInt(string key, int defaultValue = 0)
{
if (Prefs.ContainsKey(key))
return Convert.ToInt32(Prefs[key]);
return defaultValue;
}
public static float GetFloat(string key, float defaultValue = 0.0f)
{
if (Prefs.ContainsKey(key))
return Convert.ToSingle(Prefs[key]);
return defaultValue;
}
public static double GetDouble(string key, double defaultValue = 0.0)
{
if (Prefs.ContainsKey(key))
return Convert.ToDouble(Prefs[key]);
return defaultValue;
}
private static Dictionary<string, object> Load()
{
if (!Directory.Exists(DIRECTORY))
Directory.CreateDirectory(DIRECTORY);
if (!File.Exists(FILE))
return JsonSerialization.FromJson<Dictionary<string, object>>("{}");
var json = File.ReadAllText(FILE);
var serializedDictionary = JsonSerialization.FromJson<Dictionary<string, object>>(json);
return serializedDictionary;
}
private static void Save()
{
var json = JsonSerialization.ToJson(Prefs);
File.WriteAllText(FILE, json);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fad7572312c85da4e80e115c63b2d164
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,7 @@
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal interface IMainToolbarElementRepository
{
public MainToolbarElement[] GetAll();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6fdece3308162d4994b872bcb838c72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,88 @@
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
[InitializeOnLoad]
public static class MainToolbar
{
private const string TOOLBAR_ROOT_ELEMENT_FIELD_NAME = "m_Root";
private const string TOOLBAR_CENTER_CONTAINER_NAME = "ToolbarZonePlayMode";
private const string TOOLBAR_LEFT_CONTAINER_NAME = "ToolbarZoneLeftAlign";
private const string TOOLBAR_RIGHT_CONTAINER_NAME = "ToolbarZoneRightAlign";
private const string TOOLBAR_PLAY_BUTTON_NAME = "Play";
private static Type _toolbarType = typeof(Editor).Assembly.GetType("UnityEditor.Toolbar");
private static ScriptableObject _innerToolbarObject;
public static event Action OnInitialized;
public static event Action OnRefresh;
public static VisualElement UnityToolbarRoot { get; private set; }
public static VisualElement LeftContainer { get; private set; }
public static VisualElement CenterContainer { get; private set; }
public static VisualElement RightContainer { get; private set; }
public static VisualElement PlayModeButtonsContainer { get; private set; }
public static bool IsAvailable => _innerToolbarObject != null;
private static bool _initialized;
static MainToolbar()
{
EditorApplication.update -= OnUpdate;
EditorApplication.update += OnUpdate;
}
private static void WrapNativeToolbar()
{
FindUnityToolbar();
if (_innerToolbarObject == null)
return;
CacheNativeToolbarContainers();
if(!_initialized)
{
_initialized = true;
OnInitialized?.Invoke();
}
else
OnRefresh?.Invoke();
}
private static void FindUnityToolbar()
{
var toolbars = Resources.FindObjectsOfTypeAll(_toolbarType);
_innerToolbarObject = toolbars.Length > 0 ? (ScriptableObject)toolbars[0] : null;
}
private static void CacheNativeToolbarContainers()
{
var unityToolbarRootFieldInfo = _innerToolbarObject.GetType()
.GetField(TOOLBAR_ROOT_ELEMENT_FIELD_NAME, BindingFlags.NonPublic | BindingFlags.Instance);
UnityToolbarRoot = unityToolbarRootFieldInfo.GetValue(_innerToolbarObject) as VisualElement;
LeftContainer = UnityToolbarRoot.Q(TOOLBAR_LEFT_CONTAINER_NAME);
CenterContainer = UnityToolbarRoot.Q(TOOLBAR_CENTER_CONTAINER_NAME);
RightContainer = UnityToolbarRoot.Q(TOOLBAR_RIGHT_CONTAINER_NAME);
PlayModeButtonsContainer = CenterContainer.Q(TOOLBAR_PLAY_BUTTON_NAME).parent;
}
private static void OnUpdate()
{
if (NeedsWrap())
{
WrapNativeToolbar();
}
}
private static bool NeedsWrap()
{
return _innerToolbarObject == null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce77a44ce866ce34d9ce10f93b19c256
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,330 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine.UIElements;
using System.Reflection;
using UnityEngine;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
namespace Paps.UnityToolbarExtenderUIToolkit
{
[InitializeOnLoad]
public static class MainToolbarAutomaticExtender
{
private static MainToolbarElement[] _mainToolbarElements = new MainToolbarElement[0];
private static MainToolbarElement[] _groupElements = new MainToolbarElement[0];
private static GroupDefinition[] _groupDefinitions = new GroupDefinition[0];
private static MainToolbarElement[] _rootElements = new MainToolbarElement[0];
private static NativeToolbarElement[] _nativeElements = new NativeToolbarElement[0];
private static MainToolbarElement[] _singleElements = new MainToolbarElement[0];
private static MainToolbarElementOverrideApplier _overrideApplier = new MainToolbarElementOverrideApplier(ServicesAndRepositories.MainToolbarElementOverridesRepository);
private static Dictionary<string, MainToolbarElement[]> _elementsByGroup = new Dictionary<string, MainToolbarElement[]>();
private static MainToolbarElementVariableWatcher _variableWatcher = new MainToolbarElementVariableWatcher(ServicesAndRepositories.MainToolbarElementVariableRepository, ServicesAndRepositories.ValueSerializer);
internal static MainToolbarCustomContainer LeftCustomContainer { get; private set; } = new MainToolbarCustomContainer("ToolbarAutomaticExtenderLeftContainer", FlexDirection.RowReverse);
internal static MainToolbarCustomContainer RightCustomContainer { get; private set; } = new MainToolbarCustomContainer("ToolbarAutomaticExtenderRightContainer", FlexDirection.Row);
internal static MainToolbarElement[] CustomMainToolbarElements => _mainToolbarElements.ToArray();
internal static MainToolbarElement[] GroupElements => _groupElements.ToArray();
internal static NativeToolbarElement[] NativeElements => _nativeElements.ToArray();
public static event Action OnRefresh;
public static event Action OnAddedCustomContainersToToolbar;
static MainToolbarAutomaticExtender()
{
MainToolbar.OnInitialized += Initialize;
}
private static void Initialize()
{
BuildCustomToolbarContainers();
if (_mainToolbarElements.Length == 0)
return;
EditorApplication.projectChanged += OnProjectChange;
MainToolbar.OnRefresh += ApplyFixedChangesToToolbar;
CacheNativeElements();
ApplyFixedChangesToToolbar();
}
private static void CacheNativeElements()
{
var nativeElements = GetNativeElements();
if (nativeElements.Length != 0)
{
_nativeElements = nativeElements;
_overrideApplier.SetNativeElements(_nativeElements);
}
}
internal static void Refresh()
{
if (!MainToolbar.IsAvailable)
return;
ResetCustomContainers();
BuildCustomToolbarContainers();
_overrideApplier.ApplyOverrides();
OnRefresh?.Invoke();
}
internal static MainToolbarElement[] GetElementsOfGroup(string id)
{
return _elementsByGroup[id].ToArray();
}
private static void ResetCustomContainers()
{
LeftCustomContainer.ClearContainer();
RightCustomContainer.ClearContainer();
}
private static void BuildCustomToolbarContainers()
{
_mainToolbarElements = ServicesAndRepositories.MainToolbarElementRepository.GetAll();
if (_mainToolbarElements.Count() == 0)
return;
_groupDefinitions = LoadGroupDefinitions();
_groupElements = GetGroups();
_elementsByGroup = GetElementsByGroup();
InitializeGroups();
_singleElements = GetSingles();
_rootElements = GetRootElements();
_overrideApplier.SetCustomElements(_mainToolbarElements.Concat(_groupElements).ToArray());
_variableWatcher.RestoreValues(CustomMainToolbarElements);
InitializeCustomElements();
RegisterElementsForRecommendedStyles();
AddRootElementsToContainers();
EditorApplication.update += Update;
}
private static void InitializeCustomElements()
{
foreach(var customElement in CustomMainToolbarElements)
{
var typeOfElement = customElement.VisualElement.GetType();
var initializeMethod = typeOfElement.GetMethod("InitializeElement", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
initializeMethod?.Invoke(customElement.VisualElement, null);
}
}
private static void Update()
{
_variableWatcher.Update();
}
private static void InitializeGroups()
{
foreach (var group in _groupElements)
{
var groupElement = group.VisualElement as GroupElement;
var elementsOfThisGroup = _elementsByGroup[group.Id]
.Select(e => e.VisualElement)
.ToArray();
groupElement.Initialize(elementsOfThisGroup);
}
}
private static void RegisterElementsForRecommendedStyles()
{
var eligibleElements = _mainToolbarElements
.Concat(_groupElements)
.Where(element => element.UseRecommendedStyles)
.Select(element => new RecommendedStyleVisualElement(element.VisualElement, IsInGroup(element)))
.ToArray();
RecommendedStyles.SetElements(eligibleElements);
}
private static bool IsInGroup(MainToolbarElement mainToolbarElement)
{
return !_rootElements.Contains(mainToolbarElement);
}
private static MainToolbarElement[] GetRootElements()
{
return GetRootGroups()
.Concat(_singleElements)
.ToArray();
}
private static MainToolbarElement[] GetRootGroups()
{
var rootGroups = new List<MainToolbarElement>();
var allGroupElements = _groupElements.Select(group => group.VisualElement as GroupElement);
foreach (var group in _groupElements)
{
var groupElement = group.VisualElement as GroupElement;
if (!allGroupElements.Any(g => g.GroupedElements.Contains(groupElement)))
rootGroups.Add(group);
}
return rootGroups.ToArray();
}
private static NativeToolbarElement[] GetNativeElements()
{
return MainToolbar.LeftContainer.Children()
.Concat(MainToolbar.RightContainer.Children())
.Concat(MainToolbar.PlayModeButtonsContainer.Children())
.Where(visualElement => UnityNativeElementsIds.IdOf(visualElement) != null)
.Select(visualElement => new NativeToolbarElement(UnityNativeElementsIds.IdOf(visualElement), visualElement))
.ToArray();
}
private static void ApplyFixedChangesToToolbar()
{
ConfigureStyleOfContainers();
AddCustomContainers();
_overrideApplier.ApplyOverrides();
OnAddedCustomContainersToToolbar?.Invoke();
}
private static void AddCustomContainers()
{
MainToolbar.CenterContainer.Insert(0, LeftCustomContainer);
MainToolbar.CenterContainer.Add(RightCustomContainer);
}
private static void AddRootElementsToContainers()
{
var leftElements = _rootElements.Where(el => el.Alignment == ToolbarAlign.Left)
.OrderBy(el => el.Order);
var rightElements = _rootElements.Where(el => el.Alignment == ToolbarAlign.Right)
.OrderBy(el => el.Order);
foreach (var orderedAlignedElement in leftElements)
LeftCustomContainer.AddToContainer(orderedAlignedElement.VisualElement);
foreach (var orderedAlignedElement in rightElements)
RightCustomContainer.AddToContainer(orderedAlignedElement.VisualElement);
}
private static MainToolbarElement[] GetGroups()
{
var groups = new List<MainToolbarElement>();
foreach (var groupDefinition in _groupDefinitions)
{
if (groupDefinition.ToolbarElementsIds.Length == 0)
continue;
var groupToolbarElement = new MainToolbarElement(
groupDefinition.GroupId,
new GroupElement(groupDefinition.GroupName),
groupDefinition.Alignment,
groupDefinition.Order,
true
);
groups.Add(groupToolbarElement);
}
return groups.ToArray();
}
private static MainToolbarElement[] GetSingles()
{
var elementsInGroups = _elementsByGroup.Values.SelectMany(list => list);
return _mainToolbarElements
.Where(mainToolbarElement => !elementsInGroups.Contains(mainToolbarElement))
.ToArray();
}
private static Dictionary<string, MainToolbarElement[]> GetElementsByGroup()
{
var elementsByGroup = new Dictionary<string, MainToolbarElement[]>();
foreach (var groupDefinition in _groupDefinitions)
{
var elementsOfThisGroup = groupDefinition.ToolbarElementsIds
.Select(id =>
{
var element = _mainToolbarElements.FirstOrDefault(e => e.Id == id);
if (element != null)
return element;
element = _groupElements.FirstOrDefault(e => e.Id == id);
return element;
})
.Where(mainToolbarElement => mainToolbarElement != null)
.ToList();
elementsByGroup.Add(groupDefinition.GroupId, elementsOfThisGroup.ToArray());
}
return elementsByGroup;
}
private static GroupDefinition[] LoadGroupDefinitions()
{
return ServicesAndRepositories.GroupDefinitionRepository.GetAll();
}
private static void ConfigureStyleOfContainers()
{
MainToolbar.LeftContainer.style.flexGrow = 0;
MainToolbar.LeftContainer.style.width = Length.Auto();
MainToolbar.RightContainer.style.flexGrow = 0;
MainToolbar.RightContainer.style.width = Length.Auto();
MainToolbar.CenterContainer.style.flexGrow = 1;
MainToolbar.CenterContainer.parent.style.paddingTop = 0;
MainToolbar.CenterContainer.parent.style.paddingBottom = 0;
}
private static void OnProjectChange()
{
if(ShouldRefresh())
Refresh();
}
private static bool ShouldRefresh()
{
return GroupsChanged();
}
private static bool GroupsChanged()
{
var groups = ServicesAndRepositories.GroupDefinitionRepository.GetAll();
if (_groupDefinitions.Length != groups.Length)
return true;
if (_groupDefinitions.Length == 0 && groups.Length == 0)
return false;
for(int i = 0; i < _groupDefinitions.Length; i++)
{
var savedGroupDefinition = _groupDefinitions[i];
var retrievedGroupDefinition = groups[i];
if (!savedGroupDefinition.AreEquals(retrievedGroupDefinition))
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fd1abb4edafb6a4793209e0f154d8a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,95 @@
using System.Linq;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class MainToolbarCustomContainer : VisualElement
{
private const string LAST_SCROLLER_POSITION_SAVE_KEY_BASE = "main-toolbar-custom-container:last-scroller-position:";
private const float SCROLL_VIEW_SCROLLER_HEIGHT = 1;
private const float SCROLLER_HEIGHT = 5;
private const float SCROLL_VIEW_HORIZONTAL_PADDING = 5;
private const float SCROLL_VIEW_SCROLLER_BORDER_TOP_WIDTH = 0;
private string _id;
private ScrollView _scrollView;
private Scroller _scroller;
private VisualElement _container;
public MainToolbarCustomContainer(string id, FlexDirection flexDirection)
{
_id = id;
name = id;
style.flexDirection = flexDirection;
style.flexGrow = 1;
style.width = 0;
_container = CreateAndAddContainer(flexDirection);
}
private VisualElement CreateAndAddContainer(FlexDirection flexDirection)
{
_scrollView = new ScrollView(ScrollViewMode.Horizontal);
_scrollView.style.paddingLeft = SCROLL_VIEW_HORIZONTAL_PADDING;
_scrollView.style.paddingRight = SCROLL_VIEW_HORIZONTAL_PADDING;
_scrollView.verticalScrollerVisibility = ScrollerVisibility.Hidden;
_scrollView.contentContainer.style.flexDirection = flexDirection;
_scroller = _scrollView.horizontalScroller;
var leftButton = _scroller.lowButton;
var rightButton = _scroller.highButton;
var slider = _scroller.slider;
_scroller.style.height = SCROLL_VIEW_SCROLLER_HEIGHT;
_scroller.style.borderTopWidth = SCROLL_VIEW_SCROLLER_BORDER_TOP_WIDTH;
leftButton.style.height = SCROLLER_HEIGHT;
rightButton.style.height = SCROLLER_HEIGHT;
slider.style.height = SCROLLER_HEIGHT;
Add(_scrollView);
_scrollView.RegisterCallback<GeometryChangedEvent>(LoadLastScrollerPosition);
return _scrollView;
}
private void LoadLastScrollerPosition(GeometryChangedEvent eventArgs)
{
_scroller.value = (float)LastScrollerPosition();
_scroller.valueChanged += SaveScrollerPosition;
_scrollView.UnregisterCallback<GeometryChangedEvent>(LoadLastScrollerPosition);
}
private double LastScrollerPosition()
{
return UserSettingsPrefs.GetDouble(GetFullKey(), 0d);
}
private void SaveScrollerPosition(float newPosition)
{
UserSettingsPrefs.SetDouble(GetFullKey(), newPosition);
}
private string GetFullKey() => LAST_SCROLLER_POSITION_SAVE_KEY_BASE + _id;
public void AddToContainer(VisualElement child)
{
_container.Add(child);
}
public void ClearContainer()
{
_container.Clear();
}
public VisualElement[] GetContainerChilds()
{
return _container.Children().ToArray();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3a0122c91e132b8478c5d18c85d0df6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class MainToolbarElement
{
public string Id { get; }
public VisualElement VisualElement { get; }
public ToolbarAlign Alignment { get; }
public int Order { get; }
public bool UseRecommendedStyles { get; }
public MainToolbarElement(string id, VisualElement visualElement,
ToolbarAlign alignment, int order, bool useRecommendedStyles)
{
Id = id;
VisualElement = visualElement;
Alignment = alignment;
Order = order;
UseRecommendedStyles = useRecommendedStyles;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f67ca04efaf7f044fa5a17a7627aca0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using System;
namespace Paps.UnityToolbarExtenderUIToolkit
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class MainToolbarElementAttribute : Attribute
{
public string Id { get; }
public ToolbarAlign Alignment { get; }
public int Order { get; }
public bool UseRecommendedStyles { get; }
/// <summary>
/// Mark a class derived from VisualElement to be found by toolbar extender
/// </summary>
/// <param name="id">Id of element. Must be unique. In case of collision, first found is used</param>
/// <param name="alignment">Left or right to play buttons. Ignored if inside a group</param>
/// <param name="order">Order in which this element will be displayed in toolbar. Ignored if inside a group</param>
/// <param name="useRecommendedStyles">True if this element should use recommended styles. Set it to false if you want to style the visual element yourself.</param>
/// <param name="name"></param>
public MainToolbarElementAttribute(string id, ToolbarAlign alignment = ToolbarAlign.Left,
int order = 0,
bool useRecommendedStyles = true)
{
Id = id;
Alignment = alignment;
Order = order;
UseRecommendedStyles = useRecommendedStyles;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4059a4ac36791549810df04f72dcc75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class MainToolbarElementOverrideApplier
{
private class HiddenRemovedElement
{
public VisualElement RemovedVisualElement;
public VisualElement Parent;
public int Index;
}
private static readonly string[] EXCEPTIONAL_ELEMENTS_FOR_VISIBILITY =
{
UnityNativeElementsIds.ACCOUNT_DROPDOWN_ID,
UnityNativeElementsIds.CLOUD_BUTTON_ID
};
private readonly IMainToolbarElementOverrideRepository _mainToolbarElementOverrideRepository;
private MainToolbarElement[] _mainToolbarElements = new MainToolbarElement[0];
private NativeToolbarElement[] _nativeElements = new NativeToolbarElement[0];
private readonly Dictionary<string, MainToolbarElementOverride> _nativeElementsInitialState = new Dictionary<string, MainToolbarElementOverride>();
private readonly Dictionary<string, HiddenRemovedElement> _hiddenElementsByRemotion = new Dictionary<string, HiddenRemovedElement>();
public MainToolbarElementOverrideApplier(IMainToolbarElementOverrideRepository mainToolbarElementOverrideRepository)
{
_mainToolbarElementOverrideRepository = mainToolbarElementOverrideRepository;
}
public void SetNativeElements(NativeToolbarElement[] nativeElements)
{
_nativeElements = nativeElements.ToArray();
SaveNativeElementsInitialState();
SetNativeElementsDefaultState(); // necesito hacer esto?
}
public void SetCustomElements(MainToolbarElement[] mainToolbarElements)
{
_mainToolbarElements = mainToolbarElements.ToArray();
}
public void ApplyOverrides()
{
SetNativeElementsDefaultState();
ApplyOverridesOnCustomElements();
ApplyOverridesOnNativeElements();
}
private void ApplyOverridesOnCustomElements()
{
foreach (var mainToolbarElement in _mainToolbarElements)
{
ApplyOverride(mainToolbarElement.Id, mainToolbarElement.VisualElement);
}
}
private void ApplyOverridesOnNativeElements()
{
foreach (var nativeElement in _nativeElements)
{
ApplyOverride(nativeElement.Id, nativeElement.VisualElement);
}
}
private void SaveNativeElementsInitialState()
{
if (_nativeElementsInitialState.Count > 0)
return;
foreach (var nativeElement in _nativeElements)
{
_nativeElementsInitialState[nativeElement.Id] =
new MainToolbarElementOverride(
nativeElement.Id,
nativeElement.VisualElement.resolvedStyle.display == DisplayStyle.Flex
);
}
}
private void SetNativeElementsDefaultState()
{
foreach (var nativeElement in _nativeElements)
{
var defaultStateOverride = _nativeElementsInitialState[nativeElement.Id];
ApplyOverride(nativeElement, defaultStateOverride);
}
}
private void ApplyOverride(NativeToolbarElement nativeElement, MainToolbarElementOverride overrideData)
{
ApplyVisibilityOverride(nativeElement.Id, nativeElement.VisualElement, overrideData.Visible);
}
private void ApplyOverride(string id, VisualElement visualElement)
{
var userOverride = _mainToolbarElementOverrideRepository
.Get(id);
if (userOverride == null)
return;
ApplyVisibilityOverride(id, visualElement, userOverride.Value.Visible);
}
private void ApplyVisibilityOverride(string elementId, VisualElement visualElement, bool visible)
{
if (IsExceptionElementForVisibility(elementId))
HandleVisibilityApplicationOnExceptions(elementId, visualElement, visible);
else
visualElement.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
}
private bool IsExceptionElementForVisibility(string elementId)
{
return EXCEPTIONAL_ELEMENTS_FOR_VISIBILITY.Contains(elementId);
}
private void HandleVisibilityApplicationOnExceptions(string elementId, VisualElement visualElement, bool visible)
{
if (visible)
HandleExceptionalElementVisibleCase(elementId, visualElement);
else
HandleExceptionalElementInvisibleCase(elementId, visualElement);
}
private void HandleExceptionalElementInvisibleCase(string elementId, VisualElement visualElement)
{
var parent = visualElement.parent;
var index = parent.IndexOf(visualElement);
if (parent.Contains(visualElement))
parent.Remove(visualElement);
if (!_hiddenElementsByRemotion.ContainsKey(elementId))
_hiddenElementsByRemotion.Add(elementId, new HiddenRemovedElement()
{
RemovedVisualElement = visualElement,
Parent = parent,
Index = index
});
}
private void HandleExceptionalElementVisibleCase(string elementId, VisualElement visualElement)
{
if (_hiddenElementsByRemotion.ContainsKey(elementId))
{
var removedElement = _hiddenElementsByRemotion[elementId];
var parent = removedElement.Parent;
if (!parent.Contains(visualElement))
{
var bestIndex = GetIndexEqualOrLessThan(removedElement.Index, parent);
parent.Insert(bestIndex, visualElement);
}
_hiddenElementsByRemotion.Remove(elementId);
}
}
private int GetIndexEqualOrLessThan(int removedElementIndex, VisualElement parent)
{
for (int i = parent.childCount - 1; i >= 0; i--)
{
if (removedElementIndex <= i)
return i;
}
return 0;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 24d310911f14d4d45a02bd5586c371bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class NativeToolbarElement
{
public string Id { get; }
public VisualElement VisualElement { get; }
public NativeToolbarElement(string id, VisualElement visualElement)
{
Id = id;
VisualElement = visualElement;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b9dec15dbb8eea42b0c2254ce4b7177
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6f722a3e209ed5649a219e2282177125
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,10 @@
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal interface IMainToolbarElementOverrideRepository
{
public MainToolbarElementOverride? Get(string elementId);
public MainToolbarElementOverride[] GetAll();
public void Save(MainToolbarElementOverride elementOverride);
public void Clear();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 280c883dcd1944448a1b93cecc8877f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal readonly struct MainToolbarElementOverride
{
public string ElementId { get; }
public bool Visible { get; }
public MainToolbarElementOverride(string elementId, bool visible)
{
ElementId = elementId;
Visible = visible;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e51009f5c83b62f419eee2089fd6e63f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,99 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.Serialization.Json;
using UnityEngine;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class UserSettingsFileMainToolbarElementOverrideRepository : IMainToolbarElementOverrideRepository
{
private static readonly string DIRECTORY = Path.Combine(Directory.GetParent(Application.dataPath).FullName, "UserSettings/", "unity-toolbar-extender-ui-toolkit", "overrides");
private static readonly string FILE = Path.Combine(DIRECTORY, "overrides.json");
private struct SerializableOverride
{
public string ElementId;
public bool Visible;
}
private Dictionary<string, MainToolbarElementOverride> _overrides = new Dictionary<string, MainToolbarElementOverride>();
public UserSettingsFileMainToolbarElementOverrideRepository()
{
_overrides = LoadOverrides();
}
public void Clear()
{
_overrides.Clear();
DeleteSave();
}
public MainToolbarElementOverride? Get(string elementId)
{
if(_overrides.ContainsKey(elementId))
return _overrides[elementId];
return null;
}
public MainToolbarElementOverride[] GetAll()
{
return _overrides.Values.ToArray();
}
public void Save(MainToolbarElementOverride elementOverride)
{
_overrides[elementOverride.ElementId] = elementOverride;
SaveOverrides();
}
private MainToolbarElementOverride FromSerialized(SerializableOverride serializableOverride)
{
return new MainToolbarElementOverride(serializableOverride.ElementId, serializableOverride.Visible);
}
private SerializableOverride ToSerialized(MainToolbarElementOverride mainToolbarElementOverride)
{
return new SerializableOverride() {
ElementId = mainToolbarElementOverride.ElementId,
Visible = mainToolbarElementOverride.Visible
};
}
private void DeleteSave()
{
File.Delete(FILE);
}
private Dictionary<string, MainToolbarElementOverride> LoadOverrides()
{
if (!Directory.Exists(DIRECTORY))
Directory.CreateDirectory(DIRECTORY);
if (!File.Exists(FILE))
return JsonSerialization.FromJson<Dictionary<string, MainToolbarElementOverride>>("{}");
var json = File.ReadAllText(FILE);
var serializedDictionary = JsonSerialization.FromJson<Dictionary<string, SerializableOverride>>(json);
return serializedDictionary.Values
.ToDictionary(serializedOverride => serializedOverride.ElementId,
serializedOverride => FromSerialized(serializedOverride));
}
private void SaveOverrides()
{
var serializableDictionary = _overrides.Values.ToDictionary(
mainToolbarElementOverride => mainToolbarElementOverride.ElementId,
mainToolbarElementOverride => ToSerialized(mainToolbarElementOverride)
);
var json = JsonSerialization.ToJson(serializableDictionary);
File.WriteAllText(FILE, json);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 267c04a3d47600c488137859ea7af2a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
{
"name": "Paps.UnityToolbarExtenderUIToolkit",
"rootNamespace": "Paps.UnityToolbarExtenderUIToolkit",
"references": [
"Unity.Serialization"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8d62da4aabd2a19419c7378d23ea5849
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 21a21acb2ce38fb46bd178a11e0cc483
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class ButtonRecommendedStyle : RecommendedStyle
{
private Button _button;
public ButtonRecommendedStyle(Button button)
{
_button = button;
}
protected override void ApplyRootElementStyle()
{
_button.style.overflow = Overflow.Visible;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcaec52f220ee13458fa862e8f9f31b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,20 @@
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class ColorFieldRecommendedStyle : RecommendedStyle
{
private readonly ColorField _colorField;
public ColorFieldRecommendedStyle(ColorField colorField)
{
_colorField = colorField;
}
protected override void ApplyRootElementStyle()
{
_colorField.labelElement.style.minWidth = Length.Auto();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee22b5ac49a830e4a8e35f5e98b656d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using UnityEngine.UIElements;
namespace Paps.UnityToolbarExtenderUIToolkit
{
internal class DropdownFieldRecommendedStyle : RecommendedStyle
{
private DropdownField _dropdownField;
public DropdownFieldRecommendedStyle(DropdownField dropdownField)
{
_dropdownField = dropdownField;
}
protected override void ApplyRootElementStyle()
{
_dropdownField.labelElement.style.minWidth = Length.Auto();
var inputFieldIndex = 1;
if (string.IsNullOrEmpty(_dropdownField.label))
inputFieldIndex = 0;
var inputElement = _dropdownField[inputFieldIndex];
inputElement.style.overflow = Overflow.Visible;
}
}
}

Some files were not shown because too many files have changed in this diff Show More