Skip to content

Commit

Permalink
Add separate default handler rather than adding the default after the…
Browse files Browse the repository at this point in the history
… event
  • Loading branch information
Banane9 committed Jul 18, 2024
1 parent 1239d30 commit dca1ee5
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 140 deletions.
4 changes: 2 additions & 2 deletions ComponentSelectorAdditions/ComponentSelectorAdditions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
<PackageId>ComponentSelectorAdditions</PackageId>
<Title>Component Selector Additions</Title>
<Authors>Banane9</Authors>
<Version>0.3.1-beta</Version>
<Version>0.4.0-beta</Version>
<Description>This MonkeyLoader mod for Resonite overhauls the Component Selector / Protoflux Node Selector to have a search, as well as favorites and recents categories.</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>LGPL-3.0-or-later</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/ResoniteModdingGroup/ComponentSelectorAdditions</PackageProjectUrl>
<PackageTags>mod; mods; monkeyloader; resonite; component; attacher; protoflux; node; picker; search; favorites</PackageTags>
<PackageTags>mod; mods; monkeyloader; resonite; component; attacher; selector; protoflux; node; picker; search; favorites</PackageTags>
</PropertyGroup>

<Target Name="CopyPackage" AfterTargets="Pack">
Expand Down
59 changes: 59 additions & 0 deletions ComponentSelectorAdditions/DefaultConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using MonkeyLoader.Configuration;
using System;

namespace ComponentSelectorAdditions
{
/// <summary>
/// Contains settings for the buttons generated by default.
/// </summary>
public sealed class DefaultConfig : ConfigSection
{
private static readonly DefiningConfigKey<float> _directButtonHeight = new("DirectButtonHeight", "The height of a button that is the direct child of the current category, in canvas units. Default is 32.", () => 32)
{
new ConfigKeyRange<float>(32, 64)
};

private static readonly DefiningConfigKey<float> _indirectButtonHeight = new("IndirectButtonHeight", "The height of a button that is not a direct child of the current category and has to fit a category path as well, in canvas units. Default is 48.", () => 48)
{
new ConfigKeyRange<float>(32, 96)
};

/// <summary>
/// Gets this config's instance.
/// </summary>
public static DefaultConfig Instance { get; private set; } = null!;

/// <inheritdoc/>
public override string Description => "Contains settings for the buttons generated by default.";

/// <summary>
/// The height of a button that is the direct child of the current category.
/// </summary>
/// <value>The height in canvas units.</value>
public float DirectButtonHeight => _directButtonHeight;

/// <inheritdoc/>
public override string Id => "Defaults";

/// <summary>
/// Gets the height of a button that is not a direct child of the current category and has to fit a category path as well.
/// </summary>
/// <value>The height in canvas units.</value>
public float IndirectButtonHeight => _indirectButtonHeight;

/// <inheritdoc/>
public override Version Version { get; } = new Version(1, 0, 0);

/// <summary>
/// Creates an instance of this config once.
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
public DefaultConfig()
{
if (Instance is not null)
throw new InvalidOperationException();

Instance = this;
}
}
}
222 changes: 222 additions & 0 deletions ComponentSelectorAdditions/DefaultHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
using ComponentSelectorAdditions.Events;
using Elements.Core;
using FrooxEngine.UIX;
using FrooxEngine;
using HarmonyLib;
using MonkeyLoader.Events;
using MonkeyLoader.Patching;
using MonkeyLoader.Resonite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace ComponentSelectorAdditions
{
public sealed class DefaultHandler : ConfiguredResoniteMonkey<DefaultHandler, DefaultConfig>,

Check warning on line 18 in ComponentSelectorAdditions/DefaultHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Missing XML comment for publicly visible type or member 'DefaultHandler'
ICancelableEventHandler<EnumerateCategoriesEvent>, ICancelableEventHandler<EnumerateComponentsEvent>,
ICancelableEventHandler<BuildCategoryButtonEvent>, ICancelableEventHandler<BuildGroupButtonEvent>, ICancelableEventHandler<BuildComponentButtonEvent>,
IEventHandler<BuildCustomGenericBuilder>, IEventHandler<EnumerateConcreteGenericsEvent>
{
/// <inheritdoc/>
public int Priority => HarmonyLib.Priority.Normal;

/// <inheritdoc/>
public bool SkipCanceled => true;

public static string? GetPrettyPath<T>(CategoryNode<T>? subCategory, CategoryNode<T>? rootCategory = null, string delimiter = " > ")

Check warning on line 29 in ComponentSelectorAdditions/DefaultHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Missing XML comment for publicly visible type or member 'DefaultHandler.GetPrettyPath<T>(CategoryNode<T>?, CategoryNode<T>?, string)'
{
var segments = EnumerateParents(subCategory, rootCategory);

if (!segments.Any())
return null;

return segments.Reverse().Join(delimiter: delimiter) + delimiter.TrimEnd();
}

public static TextField MakeGenericArgumentInput(UIBuilder ui, Type component, Type genericArgument, GenericArgumentPrefiller? genericArgumentPrefiller = null)

Check warning on line 39 in ComponentSelectorAdditions/DefaultHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Missing XML comment for publicly visible type or member 'DefaultHandler.MakeGenericArgumentInput(UIBuilder, Type, Type, GenericArgumentPrefiller?)'
{
var textField = ui.HorizontalElementWithLabel(genericArgument.Name, .05f, () =>
{
var textField = ui.TextField(null, false, null, false);
textField.Text.NullContent.AssignLocaleString(Mod.GetLocaleString("EnterType"));

return textField;
}, out var label);

label.HorizontalAlign.Value = Elements.Assets.TextHorizontalAlignment.Center;

if (genericArgumentPrefiller != null)
textField.TargetString = genericArgumentPrefiller(component, genericArgument);

return textField;
}

public static void MakePermanentButton(UIBuilder ui, string? category, LocaleString name, colorX tint, ButtonEventHandler<string> callback, string argument)

Check warning on line 57 in ComponentSelectorAdditions/DefaultHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Missing XML comment for publicly visible type or member 'DefaultHandler.MakePermanentButton(UIBuilder, string?, LocaleString, colorX, ButtonEventHandler<string>, string)'
{
ui.PushStyle();
ui.Style.MinHeight = category is not null ? ConfigSection.IndirectButtonHeight : ConfigSection.DirectButtonHeight;

var button = ui.Button(name, tint, callback, argument, .35f);
button.Label.ParseRichText.Value = false;

if (category is not null)
{
var buttonLabel = button.Label;
buttonLabel.ParseRichText.Value = false;
ui.NestInto(button.RectTransform);

var panel = ui.Panel();
panel.OffsetMin.Value = buttonLabel.RectTransform.OffsetMin;
panel.OffsetMax.Value = buttonLabel.RectTransform.OffsetMax;

ui.HorizontalHeader(ConfigSection.IndirectButtonHeight / 2.666f, out var header, out var content);

buttonLabel.Slot.Parent = content.Slot;
buttonLabel.RectTransform.OffsetMin.Value = new(ConfigSection.IndirectButtonHeight / 3, 0);
buttonLabel.RectTransform.OffsetMax.Value = float2.Zero;

ui.NestInto(header);
var text = ui.Text(category, parseRTF: false);
//text.Color.Value = RadiantUI_Constants.Neutrals.LIGHT;
}

ui.PopStyle();
}

void ICancelableEventHandler<EnumerateComponentsEvent>.Handle(EnumerateComponentsEvent eventData)
{
foreach (var type in eventData.RootCategory.Elements)
eventData.AddItem(new ComponentResult(eventData.RootCategory, type));

eventData.Canceled = true;
}

void ICancelableEventHandler<EnumerateCategoriesEvent>.Handle(EnumerateCategoriesEvent eventData)
{
if (!eventData.Path.HasGroup)
{
foreach (var subcategory in eventData.RootCategory.Subcategories)
eventData.AddItem(subcategory);
}

eventData.Canceled = true;
}

void ICancelableEventHandler<BuildGroupButtonEvent>.Handle(BuildGroupButtonEvent eventData)
{
var selector = eventData.Selector;

var category = GetPrettyPath(eventData.ItemCategory, eventData.RootCategory);
var tint = RadiantUI_Constants.Sub.PURPLE;
var argument = $"{eventData.ItemCategory.GetPath()}:{eventData.Group}";

MakePermanentButton(eventData.UI, category, eventData.GroupName, tint, selector.OpenGroupPressed, argument);

eventData.Canceled = true;
}

void IEventHandler<EnumerateConcreteGenericsEvent>.Handle(EnumerateConcreteGenericsEvent eventData)
{
foreach (var concreteGeneric in WorkerInitializer.GetCommonGenericTypes(eventData.Component))
eventData.AddItem(concreteGeneric);
}

void IEventHandler<BuildCustomGenericBuilder>.Handle(BuildCustomGenericBuilder eventData)
{
var ui = eventData.UI;
var selector = eventData.Selector;

if (!eventData.AddsGenericArgumentInputs)
{
foreach (var genericArgument in eventData.GenericArguments)
{
var textField = MakeGenericArgumentInput(ui, eventData.Component, genericArgument, selector.GenericArgumentPrefiller.Target);

selector._customGenericArguments.Add(textField);
}
}

if (!eventData.AddsCreateCustomTypeButton)
eventData.CreateCustomTypeButton = ui.Button((LocaleString)string.Empty, RadiantUI_Constants.BUTTON_COLOR, selector.OnCreateCustomType, .35f);
}

void ICancelableEventHandler<BuildComponentButtonEvent>.Handle(BuildComponentButtonEvent eventData)
{
var path = eventData.Path;
var selector = eventData.Selector;
var component = eventData.Component;

var category = GetPrettyPath(component.Category, eventData.RootCategory);
var tint = component.IsGeneric ? RadiantUI_Constants.Sub.GREEN : RadiantUI_Constants.Sub.CYAN;
ButtonEventHandler<string> callback = component.IsGeneric ? selector.OpenGenericTypesPressed : selector.OnAddComponentPressed;
var argument = $"{(component.IsGeneric ? $"{path.Path}/{component.Type.AssemblyQualifiedName}" : selector.World.Types.EncodeType(component.Type))}{(component.IsGeneric && path.HasGroup ? $"?{path.Group}" : "")}";

MakePermanentButton(eventData.UI, category, component.NiceName, tint, callback, argument);

eventData.Canceled = true;
}

void ICancelableEventHandler<BuildCategoryButtonEvent>.Handle(BuildCategoryButtonEvent eventData)
{
MakePermanentButton(eventData.UI, null,
GetPrettyPath(eventData.ItemCategory, eventData.RootCategory),
RadiantUI_Constants.Sub.YELLOW,
eventData.Selector.OnOpenCategoryPressed,
eventData.ItemCategory.GetPath());

eventData.Canceled = true;
}

/// <inheritdoc/>
protected override IEnumerable<IFeaturePatch> GetFeaturePatches() => Enumerable.Empty<IFeaturePatch>();

/// <inheritdoc/>
protected override bool OnLoaded()
{
Mod.RegisterEventHandler<EnumerateCategoriesEvent>(this);
Mod.RegisterEventHandler<EnumerateComponentsEvent>(this);

Mod.RegisterEventHandler<BuildCategoryButtonEvent>(this);
Mod.RegisterEventHandler<BuildGroupButtonEvent>(this);
Mod.RegisterEventHandler<BuildComponentButtonEvent>(this);

Mod.RegisterEventHandler<BuildCustomGenericBuilder>(this);
Mod.RegisterEventHandler<EnumerateConcreteGenericsEvent>(this);

return base.OnLoaded();
}

/// <inheritdoc/>
protected override bool OnShutdown(bool applicationExiting)
{
if (!applicationExiting)
{
Mod.UnregisterEventHandler<EnumerateCategoriesEvent>(this);
Mod.UnregisterEventHandler<EnumerateComponentsEvent>(this);

Mod.UnregisterEventHandler<BuildCategoryButtonEvent>(this);
Mod.UnregisterEventHandler<BuildGroupButtonEvent>(this);
Mod.UnregisterEventHandler<BuildComponentButtonEvent>(this);

Mod.UnregisterEventHandler<BuildCustomGenericBuilder>(this);
Mod.UnregisterEventHandler<EnumerateConcreteGenericsEvent>(this);
}

return base.OnShutdown(applicationExiting);
}

private static IEnumerable<string> EnumerateParents<T>(CategoryNode<T>? start, CategoryNode<T>? end = null)
{
var current = start;

while (current is not null && current != end)
{
yield return current.Parent is null ? "" : current.Name;
current = current.Parent;
}
}
}
}
2 changes: 1 addition & 1 deletion ComponentSelectorAdditions/FavoritesCategories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal sealed class FavoritesCategories : ConfiguredResoniteMonkey<FavoritesCa
private CategoryNode<Type> _rootCategory = null!;

public override bool CanBeDisabled => true;
public int Priority => HarmonyLib.Priority.Normal;
public int Priority => HarmonyLib.Priority.High;

public bool SkipCanceled => true;

Expand Down
15 changes: 2 additions & 13 deletions ComponentSelectorAdditions/GenericPresetsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal sealed class GenericPresetsHandler : ConfiguredResoniteMonkey<GenericPr
IEventHandler<BuildCustomGenericBuilder>, IEventHandler<EnumerateConcreteGenericsEvent>
{
public override bool CanBeDisabled => true;
public int Priority => HarmonyLib.Priority.Normal;
public int Priority => HarmonyLib.Priority.High;

public void Handle(EnumerateConcreteGenericsEvent eventData)
{
Expand Down Expand Up @@ -54,18 +54,7 @@ public void Handle(BuildCustomGenericBuilder eventData)

foreach (var genericArgument in eventData.GenericArguments)
{
var textField = ui.HorizontalElementWithLabel(genericArgument.Name, .05f, () =>
{
var textField = ui.TextField(null, false, null, false);
textField.Text.NullContent.AssignLocaleString(Mod.GetLocaleString("EnterType"));

return textField;
}, out var label);

label.HorizontalAlign.Value = Elements.Assets.TextHorizontalAlignment.Center;

if (selector.GenericArgumentPrefiller.Target != null)
textField.TargetString = selector.GenericArgumentPrefiller.Target(eventData.Component, genericArgument);
var textField = DefaultHandler.MakeGenericArgumentInput(ui, eventData.Component, genericArgument, selector.GenericArgumentPrefiller.Target);

selector._customGenericArguments.Add(textField);
}
Expand Down
Loading

0 comments on commit dca1ee5

Please sign in to comment.