Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ComponentsDataFeed to enumerate the component library, add ButtonAttachComponent, cleanup stuff #36

Merged
merged 3 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using FrooxEngine;
using FrooxEngine.Undo;

namespace Obsidian;

[Category(new string[] { "Obsidian/Common UI/Button Interactions" })]
public class ButtonAttachComponent : Component, IButtonPressReceiver, IComponent, IComponentBase, IDestroyable, IWorker, IWorldElement, IUpdatable, IChangeable, IAudioUpdatable, IInitializable, ILinkable
{
public readonly SyncRef<Slot> TargetSlot;

public readonly SyncType ComponentType;

public readonly Sync<bool> Undoable;

protected override void OnAttach()
{
base.OnAwake();
Undoable.Value = true;
}

public void Pressed(IButton button, ButtonEventData eventData)
{
Slot target = TargetSlot.Target;
Type componentType = ComponentType.Value;
if (target != null && componentType != null && componentType.ContainsGenericParameters == false)
{
var comp = target.AttachComponent(componentType);
if (Undoable)
{
comp.CreateSpawnUndoPoint();
}
}
}

public void Pressing(IButton button, ButtonEventData eventData)
{
}

public void Released(IButton button, ButtonEventData eventData)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,37 @@ public class ComponentData

public Component component;

private Type _componentType;

public bool Submitted { get; private set; }

public string MainName
{
get
{
return component.Name;
return component?.Name ?? _componentType.Name;
}
}

public string uniqueId;

public int MemberCount => members.Count;

public Type ComponentType => component?.GetType() ?? _componentType;

public bool IsGenericType => ComponentType.IsGenericType;

public Type GenericTypeDefinition => IsGenericType ? ComponentType.GetGenericTypeDefinition() : null;

public ComponentData(Component component)
{
this.component = component;
_componentType = component.GetType();
}

public ComponentData(Type type)
{
this._componentType = type;
}

public void MarkSubmitted()
Expand Down Expand Up @@ -87,12 +103,9 @@ public bool MatchesSearchParameters(List<string> optionalTerms, List<string> req

public bool MatchesTerm(string term)
{
if (component != null)
if (ContainsTerm(MainName, term))
{
if (ContainsTerm(component.Name, term))
{
return true;
}
return true;
}
return false;
}
Expand All @@ -108,6 +121,6 @@ private static bool ContainsTerm(string str, string term)

public override string ToString()
{
return $"Name: {MainName}, ReferenceID: {component.ReferenceID}, Members: {members.Count}";
return $"Name: {MainName}, UniqueID: {uniqueId}, MemberCount: {MemberCount}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class ComponentDataFeedItem : DataFeedItem

public ComponentDataFeedItem(ComponentData componentData)
{
InitBase(componentData.component.ReferenceID.ToString(), null, null, componentData.MainName);
InitBase(componentData.uniqueId, null, null, componentData.MainName);
Data = componentData;
foreach (ISyncMember member in componentData.members)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Elements.Core;
using FrooxEngine;
using SkyFrost.Base;

namespace Obsidian;

// This feed has two functions.

// If TargetSlot has a reference, it returns the components on that slot, optionally also returning components on children slots (IncludeChildrenSlots bool)
// It also returns the sync members (fields, lists etc) as DataFeedEntity<ISyncMember>

// If TargetSlot is null, it returns the components from the component library, which includes the categories (DataFeedCategoryItem)
// When enumerating the component library, the Component reference on the ComponentDataItemInterface will be null and there will be no members

[Category(new string[] { "Obsidian/Radiant UI/Data Feeds/Feeds" })]
public class ComponentsDataFeed : Component, IDataFeedComponent, IDataFeed, IWorldElement
{
Expand All @@ -19,23 +28,32 @@ public class ComponentsDataFeed : Component, IDataFeedComponent, IDataFeed, IWor

private Slot _lastSlot = null;

private void AddComponent(Component c)
private static HashSet<Type> _componentTypes = new();

private static bool SearchStringValid(string str)
{
return !string.IsNullOrWhiteSpace(str) && str.Length >= 3;
}

private void OnSlotComponentAdded(Component c)
{
// If local elements are written to synced fields it can cause exceptions and crashes
if (c.IsLocalElement) return;
foreach (KeyValuePair<SearchPhraseFeedUpdateHandler, ComponentsDataFeedData> updateHandler in _updateHandlers)
{
var data = updateHandler.Value.RegisterComponent(c);
foreach (ISyncMember syncMember in data.component.SyncMembers)
var result = updateHandler.Value.AddComponent(c);
foreach (ISyncMember syncMember in result.data.component.SyncMembers)
{
if (syncMember.IsLocalElement) continue;
data.AddMember(syncMember);
if (FilterMember(syncMember))
{
result.data.AddMember(syncMember);
}
}
ProcessUpdate(updateHandler.Key, data);
ProcessUpdate(updateHandler.Key, result.data);
}
}

private void RemoveComponent(Component c)
private void OnSlotComponentRemoved(Component c)
{
foreach (KeyValuePair<SearchPhraseFeedUpdateHandler, ComponentsDataFeedData> updateHandler in _updateHandlers)
{
Expand All @@ -53,22 +71,28 @@ private void Update()
}
}

private bool FilterMember(ISyncMember member)
{
if (member.IsLocalElement) return false;
return true;
}

private void ProcessUpdate(SearchPhraseFeedUpdateHandler handler, ComponentData data)
{
bool flag = true;
if (!string.IsNullOrEmpty(handler.searchPhrase))
{
List<string> list = Pool.BorrowList<string>();
List<string> list2 = Pool.BorrowList<string>();
List<string> list3 = Pool.BorrowList<string>();
SearchQueryParser.Parse(handler.searchPhrase, list, list2, list3);
if (!data.MatchesSearchParameters(list, list2, list3))
List<string> optionalTerms = Pool.BorrowList<string>();
List<string> requiredTerms = Pool.BorrowList<string>();
List<string> excludedTerms = Pool.BorrowList<string>();
SearchQueryParser.Parse(handler.searchPhrase, optionalTerms, requiredTerms, excludedTerms);
if (!data.MatchesSearchParameters(optionalTerms, requiredTerms, excludedTerms))
{
flag = false;
}
Pool.Return(ref list);
Pool.Return(ref list2);
Pool.Return(ref list3);
Pool.Return(ref optionalTerms);
Pool.Return(ref requiredTerms);
Pool.Return(ref excludedTerms);
}
if (!flag)
{
Expand All @@ -86,14 +110,20 @@ private void ProcessUpdate(SearchPhraseFeedUpdateHandler handler, ComponentData

private void Subscribe(Slot s)
{
s.ComponentAdded += AddComponent;
s.ComponentRemoved += RemoveComponent;
s.ComponentAdded += OnSlotComponentAdded;
s.ComponentRemoved += OnSlotComponentRemoved;
}

private void Unsubscribe(Slot s)
{
s.ComponentAdded -= AddComponent;
s.ComponentRemoved -= RemoveComponent;
s.ComponentAdded -= OnSlotComponentAdded;
s.ComponentRemoved -= OnSlotComponentRemoved;
}

protected override void OnAwake()
{
base.OnAwake();
_lastSlot = TargetSlot.Target;
}

protected override void OnChanges()
Expand Down Expand Up @@ -156,17 +186,53 @@ protected override void OnPrepareDestroy()
}
}

public async IAsyncEnumerable<DataFeedItem> Enumerate(IReadOnlyList<string> path, IReadOnlyList<string> groupKeys, string searchPhrase, object viewData)
private void GetAllTypes(HashSet<Type> allTypes, CategoryNode<Type> categoryNode)
{
if (path != null && path.Count > 0)
foreach (var elem in categoryNode.Elements)
{
yield break;
allTypes.Add(elem);
}
if (groupKeys != null && groupKeys.Count > 0)
foreach (var subCat in categoryNode.Subcategories)
{
GetAllTypes(allTypes, subCat);
}
}

private IEnumerable<Type> EnumerateAllTypes(CategoryNode<Type> categoryNode)
{
foreach (var elem in categoryNode.Elements)
{
yield return elem;
}
foreach (var subCat in categoryNode.Subcategories)
{
foreach(var elem2 in EnumerateAllTypes(subCat))
{
yield return elem2;
}
}
}

private string GetCategoryKey(CategoryNode<Type> categoryNode)
{
return categoryNode.Name;
}

private DataFeedCategory GenerateCategory(string key, IReadOnlyList<string> path)
{
DataFeedCategory dataFeedCategory = new DataFeedCategory();
// random icon
dataFeedCategory.InitBase(key, path, null, key, OfficialAssets.Graphics.Icons.Gizmo.TransformLocal);
return dataFeedCategory;
}

public async IAsyncEnumerable<DataFeedItem> Enumerate(IReadOnlyList<string> path, IReadOnlyList<string> groupKeys, string searchPhrase, object viewData)
{
if (TargetSlot.Target != null && (path != null && path.Count > 0))
{
yield break;
}
if (TargetSlot.Target == null)
if (groupKeys != null && groupKeys.Count > 0)
{
yield break;
}
Expand All @@ -175,16 +241,77 @@ public async IAsyncEnumerable<DataFeedItem> Enumerate(IReadOnlyList<string> path
componentDataFeedData.Clear();
searchPhrase = searchPhrase?.Trim();

var components = IncludeChildrenSlots ? TargetSlot.Target.GetComponentsInChildren<Component>() : TargetSlot.Target.GetComponents<Component>();
foreach (Component allComponent in components)
if (TargetSlot.Target == null)
{
var lib = WorkerInitializer.ComponentLibrary;
if (path != null && path.Count > 0)
{
var catNode = lib;
foreach (var str in path)
{
var subCat = catNode.Subcategories.FirstOrDefault(x => x.Name == str);
if (subCat != null)
{
catNode = subCat;
}
else
{
yield break;
}
}
foreach (var subCat2 in catNode.Subcategories)
{
yield return GenerateCategory(GetCategoryKey(subCat2), path);
}
if (SearchStringValid(searchPhrase))
{
foreach (var elem in EnumerateAllTypes(catNode))
{
componentDataFeedData.AddComponentType(elem);
}
}
else
{
foreach (var elem in catNode.Elements)
{
componentDataFeedData.AddComponentType(elem);
}
}
}
else
{
if (_componentTypes.Count == 0)
{
GetAllTypes(_componentTypes, lib);
}
foreach (var subCat in lib.Subcategories)
{
yield return GenerateCategory(GetCategoryKey(subCat), path);
}
if (SearchStringValid(searchPhrase))
{
foreach (var elem in _componentTypes)
{
componentDataFeedData.AddComponentType(elem);
}
}
}
}
else
{
// If local elements are written to synced fields it can cause exceptions and crashes
if (allComponent.IsLocalElement) continue;
componentDataFeedData.RegisterComponent(allComponent);
foreach (ISyncMember syncMember in allComponent.SyncMembers)
var components = IncludeChildrenSlots ? TargetSlot.Target.GetComponentsInChildren<Component>() : TargetSlot.Target.GetComponents<Component>();
foreach (Component allComponent in components)
{
if (syncMember.IsLocalElement) continue;
componentDataFeedData.AddMember(syncMember);
// If local elements are written to synced fields it can cause exceptions and crashes
if (allComponent.IsLocalElement) continue;
var result = componentDataFeedData.AddComponent(allComponent);
foreach (ISyncMember syncMember in allComponent.SyncMembers)
{
if (FilterMember(syncMember))
{
result.data.AddMember(syncMember);
}
}
}
}

Expand Down
Loading