Skip to content

Commit

Permalink
Merge pull request #7 from Garume/v1.0.0
Browse files Browse the repository at this point in the history
Version up to 1.0.0
  • Loading branch information
Garume committed May 18, 2024
2 parents c1a8f95 + eebec3e commit a993a30
Show file tree
Hide file tree
Showing 38 changed files with 1,121 additions and 458 deletions.
4 changes: 2 additions & 2 deletions Assets/ContextCircleMenu/Editor/ContextCircleMenuLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private static void Initialize()
{
if (_contextCircleMenu != null) RemovePreviousRadialMenu();
_contextCircleMenu =
new ContextCircleMenu(RadialMenuSize.x, RadialMenuSize.y, _activeSceneView.rootVisualElement);
new ContextCircleMenu(RadialMenuSize.x, RadialMenuSize.y, 100f, _activeSceneView.rootVisualElement);

if (_onBuild == null)
_contextCircleMenu.CreateMenu(builder =>
Expand All @@ -69,7 +69,7 @@ private static void Initialize()

_activeSceneView.rootVisualElement.Add(_contextCircleMenu);
}


/// <summary>
/// Event that allows customization of the Context Circle Menu construction.
Expand Down
22 changes: 17 additions & 5 deletions Assets/ContextCircleMenu/Editor/Core/CircleMenuBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ namespace ContextCircleMenu.Editor
public sealed class CircleMenuBuilder
{
private readonly List<ICircleMenuFactory> _factories = new();
private IButtonFactory _buttonFactory;
private IFolderCircleMenuFactory _folderFactory;
private ICircleMenuFactory _rootFactory;

internal CircleMenu Build(IMenuControllable menu)
{
_rootFactory ??= new RootMenuFactory();
_folderFactory ??= new FolderMenuFactory();
_buttonFactory ??= new ButtonFactory();

var root = _rootFactory.Create();
CircleMenu root = _folderFactory.Create(string.Empty, menu, null, _buttonFactory);
foreach (var factory in _factories)
{
var pathSegments = factory.PathSegments.SkipLast(1);
Expand All @@ -27,14 +27,17 @@ internal CircleMenu Build(IMenuControllable menu)
var child = currentMenu.Children.Find(m => m.Path == pathSegment);
if (child == null)
{
child = _folderFactory.Create(pathSegment, menu, currentMenu);
child = _folderFactory.Create(pathSegment, menu, currentMenu, _buttonFactory);
var backButton = _buttonFactory.CreateBackButton(menu.Back);
backButton.ShouldCloseMenuAfterSelection = false;
child.PrepareButton(backButton);
currentMenu.Children.Add(child);
}

currentMenu = child;
}

currentMenu.Children.Add(factory.Create());
currentMenu.Children.Add(factory.Create(_buttonFactory));
}

return root;
Expand Down Expand Up @@ -78,5 +81,14 @@ public void ConfigureFolder(IFolderCircleMenuFactory factory)
{
_folderFactory = factory;
}

/// <summary>
/// Sets a custom factory for creating menu buttons, allowing for further customization of menu buttons.
/// </summary>
/// <param name="factory">The factory to use for creating menu buttons.</param>
public void ConfigureButton(IButtonFactory factory)
{
_buttonFactory = factory;
}
}
}
109 changes: 109 additions & 0 deletions Assets/ContextCircleMenu/Editor/Core/CircleMenus/CircleMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;

namespace ContextCircleMenu.Editor
{
/// <summary>
/// Represents a menu item in the circle menu.
/// </summary>
public abstract class CircleMenu
{
protected internal readonly CircleMenu Parent;

private bool _alreadyInitialized;
private IButtonFactory _buttonFactory;

protected CircleButton[] ButtonElements;
protected VisualElement[] UtilityElements;

protected CircleMenu(string path, GUIContent icon, Action onSelected, CircleMenu parent, IButtonFactory factory,
bool shouldCloseMenuAfterSelection = true)
{
Path = path;
Icon = icon;
OnSelected = onSelected;
Parent = parent;
ShouldCloseMenuAfterSelection = shouldCloseMenuAfterSelection;
_buttonFactory = factory;
}

public List<CircleMenu> Children { get; } = new(8);
public GUIContent Icon { get; }
public string Path { get; }
public bool ShouldCloseMenuAfterSelection { get; }
public Action OnSelected { get; protected set; }

internal ReadOnlySpan<VisualElement> BuildElements(ref ContextCircleMenuOption menuOption)
{
if (!_alreadyInitialized)
{
_buttonFactory ??= new ButtonFactory();
var buttons = CreateButtons(_buttonFactory, ref menuOption);
ButtonElements = ButtonElements == null ? buttons : ButtonElements.Concat(buttons).ToArray();
UtilityElements = CreateUtilityElements(ref menuOption);

OnInitialized(ref menuOption);
_alreadyInitialized = true;
}

OnBuild();

var pool = ArrayPool<VisualElement>.Shared;
var buffer = pool.Rent(ButtonElements.Length + UtilityElements.Length);
ButtonElements.CopyTo(buffer, 0);
UtilityElements.CopyTo(buffer, ButtonElements.Length);
var combinedSpan = new Span<VisualElement>(buffer, 0, ButtonElements.Length + UtilityElements.Length);
pool.Return(buffer);
return combinedSpan;
}


internal void PrepareButton(CircleButton button)
{
if (ButtonElements == null)
{
ButtonElements = new[] { button };
}
else
{
Array.Resize(ref ButtonElements, ButtonElements.Length + 1);
ButtonElements[^1] = button;
}
}

/// <summary>
/// Creates the buttons for the menu.
/// </summary>
/// <returns></returns>
protected abstract CircleButton[]
CreateButtons(IButtonFactory factory, ref ContextCircleMenuOption menuOption);

/// <summary>
/// Creates the utility elements for the menu.
/// </summary>
/// <returns></returns>
protected virtual VisualElement[] CreateUtilityElements(ref ContextCircleMenuOption menuOption)
{
return Array.Empty<VisualElement>();
}

/// <summary>
/// Called when the menu is initialized.
/// </summary>
/// <param name="menuOption"></param>
protected virtual void OnInitialized(ref ContextCircleMenuOption menuOption)
{
}

/// <summary>
/// Called when the menu is built.
/// </summary>
protected virtual void OnBuild()
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ public AttributeCircleMenuFactory(ContextCircleMenuAttribute attribute, MethodIn

public IEnumerable<string> PathSegments { get; }

public CircleMenu Create()
public CircleMenu Create(IButtonFactory factory)
{
return new LeafCircleMenu(PathSegments.Last(), _content, () => _method.Invoke(null, null));
return new LeafCircleMenu(PathSegments.Last(), _content, () => _method.Invoke(null, null), factory);
}
}

Expand All @@ -44,29 +44,17 @@ public CircleMenuFactory(string path, GUIContent content, Action action)

public IEnumerable<string> PathSegments { get; }

public CircleMenu Create()
public CircleMenu Create(IButtonFactory factory)
{
return new LeafCircleMenu(PathSegments.Last(), _content, _action);
}
}

public class RootMenuFactory : ICircleMenuFactory
{
public IEnumerable<string> PathSegments => null;

public CircleMenu Create()
{
return new RootCircleMenu();
return new LeafCircleMenu(PathSegments.Last(), _content, _action, factory);
}
}

public class FolderMenuFactory : IFolderCircleMenuFactory
{
public int Radius { get; set; } = 100;

public CircleMenu Create(string path, IMenuControllable menu, CircleMenu parent)
public FolderCircleMenu Create(string path, IMenuControllable menu, CircleMenu parent, IButtonFactory factory)
{
return new FolderCircleMenu(path, menu.Open, menu.Back, parent, Radius);
return new FolderCircleMenu(path, menu, parent, factory);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace ContextCircleMenu.Editor
{
/// <inheritdoc />
public class FolderCircleMenu : CircleMenu
{
private Vector3[] _buttonPositions;

public FolderCircleMenu(string path, IMenuControllable menu,
GUIContent icon,
CircleMenu parent,
IButtonFactory factory) :
base(path, icon, null, parent, factory, false)
{
OnSelected = () => menu.Open(this);
}

internal FolderCircleMenu(string path, IMenuControllable menu,
CircleMenu parent,
IButtonFactory factory) :
this(path, menu, EditorGUIUtility.IconContent(EditorIcons.FolderIcon), parent, factory)
{
}

/// <inheritdoc />
protected override CircleButton[] CreateButtons(IButtonFactory factory, ref ContextCircleMenuOption menuOption)
{
var buttons = new CircleButton[Children.Count];
for (var index = 0; index < buttons.Length; index++)
{
var item = Children[index];
var button = factory.Create(
item.Path,
item.Icon,
item.OnSelected,
Children.Count - index);
button.ShouldCloseMenuAfterSelection = item.ShouldCloseMenuAfterSelection;
buttons[index] = button;
}

return buttons;
}

/// <inheritdoc />
protected override VisualElement[] CreateUtilityElements(ref ContextCircleMenuOption menuOption)
{
var label = new Label(Path)
{
style =
{
marginBottom = menuOption.Height * 0.5f + 5.0f,
fontSize = 10,
unityTextAlign = TextAnchor.MiddleCenter,
color = Color.white,
textShadow = new TextShadow
{
offset = new Vector2(0.2f, 0.2f),
blurRadius = 0,
color = Color.black
}
}
};
return new VisualElement[] { label };
}

/// <inheritdoc />
protected override void OnInitialized(ref ContextCircleMenuOption menuOption)
{
var buttonCount = ButtonElements.Length;
_buttonPositions = new Vector3[buttonCount];
for (var i = 0; i < buttonCount; i++)
_buttonPositions[i] = GetPositionForIndex(i, buttonCount, menuOption.Radius);
}

/// <inheritdoc />
protected override void OnBuild()
{
for (var i = 0; i < ButtonElements.Length; i++)
{
var button = ButtonElements[i];
button.transform.position = Vector3.zero;
var to = _buttonPositions[i];
button.experimental.animation.Position(to, 100);
}
}

private Vector3 GetPositionForIndex(float index, float totalCount, float radius)
{
var angle = index / totalCount * 360f;
return new Vector2(
Mathf.Sin(angle * Mathf.Deg2Rad) * radius,
Mathf.Cos(angle * Mathf.Deg2Rad) * radius
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ namespace ContextCircleMenu.Editor
public interface ICircleMenuFactory
{
public IEnumerable<string> PathSegments { get; }
public CircleMenu Create();
public CircleMenu Create(IButtonFactory factory);
}

public interface IFolderCircleMenuFactory
{
public CircleMenu Create(string path, IMenuControllable menu, CircleMenu parent);
public FolderCircleMenu Create(string path, IMenuControllable menu, CircleMenu parent, IButtonFactory factory);
}
}
20 changes: 20 additions & 0 deletions Assets/ContextCircleMenu/Editor/Core/CircleMenus/LeafCircleMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using UnityEngine;

namespace ContextCircleMenu.Editor
{
/// <inheritdoc />
public sealed class LeafCircleMenu : CircleMenu
{
public LeafCircleMenu(string path, GUIContent icon, Action onSelected, IButtonFactory factory,
CircleMenu parent = null) : base(path, icon, onSelected, parent, factory)
{
}

/// <inheritdoc />
protected override CircleButton[] CreateButtons(IButtonFactory factory, ref ContextCircleMenuOption menuOption)
{
return null;
}
}
}
Loading

0 comments on commit a993a30

Please sign in to comment.