diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index a3db1f9..a67ee0f 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -46,7 +46,7 @@ jobs: - name: Get Version id: version run: |- - VERSION=$(dotnet build Silksong.ModMenu.csproj -getProperty:Version) + VERSION=$(dotnet build Silksong.ModMenu/Silksong.ModMenu.csproj -getProperty:Version) echo Version is $VERSION echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Upload thunderstore artifact diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100755 new mode 100644 diff --git a/Silksong.ModMenu.sln b/Silksong.ModMenu.sln index a12f5c2..1d71fb2 100644 --- a/Silksong.ModMenu.sln +++ b/Silksong.ModMenu.sln @@ -1,25 +1,31 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.14.36623.8 d17.14 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silksong.ModMenu", "Silksong.ModMenu.csproj", "{2DB486FC-5E09-737A-CED6-80241230DE8A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2DB486FC-5E09-737A-CED6-80241230DE8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2DB486FC-5E09-737A-CED6-80241230DE8A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2DB486FC-5E09-737A-CED6-80241230DE8A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2DB486FC-5E09-737A-CED6-80241230DE8A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B9C2DFAE-63A6-4317-A691-A213FAC4D73A} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.4.11519.219 insiders +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silksong.ModMenu", "Silksong.ModMenu\Silksong.ModMenu.csproj", "{2DB486FC-5E09-737A-CED6-80241230DE8A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silksong.ModMenuTesting", "Silksong.ModMenuTesting\Silksong.ModMenuTesting.csproj", "{51062B71-1574-6F9F-4C4B-238385402476}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2DB486FC-5E09-737A-CED6-80241230DE8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2DB486FC-5E09-737A-CED6-80241230DE8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2DB486FC-5E09-737A-CED6-80241230DE8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2DB486FC-5E09-737A-CED6-80241230DE8A}.Release|Any CPU.Build.0 = Release|Any CPU + {51062B71-1574-6F9F-4C4B-238385402476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51062B71-1574-6F9F-4C4B-238385402476}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51062B71-1574-6F9F-4C4B-238385402476}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51062B71-1574-6F9F-4C4B-238385402476}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B9C2DFAE-63A6-4317-A691-A213FAC4D73A} + EndGlobalSection +EndGlobal diff --git a/Directory.Build.props b/Silksong.ModMenu/Directory.Build.props similarity index 100% rename from Directory.Build.props rename to Silksong.ModMenu/Directory.Build.props diff --git a/Elements/AbstractGroup.cs b/Silksong.ModMenu/Elements/AbstractGroup.cs similarity index 76% rename from Elements/AbstractGroup.cs rename to Silksong.ModMenu/Elements/AbstractGroup.cs index 85a608a..915b891 100644 --- a/Elements/AbstractGroup.cs +++ b/Silksong.ModMenu/Elements/AbstractGroup.cs @@ -10,26 +10,50 @@ namespace Silksong.ModMenu.Elements; /// /// Common functionality shared by most entity groups. /// -public abstract class AbstractGroup : INavigableMenuEntity +public abstract class AbstractGroup : MenuDisposable, INavigableMenuEntity { private readonly VisibilityManager visibility = new(false); + /// + /// Construct an AbstractGroup. + /// + protected AbstractGroup() + { + OnDispose += () => + { + foreach (var disposable in AllEntities().OfType()) + disposable.Dispose(); + }; + } + /// public VisibilityManager Visibility => visibility; /// /// Enumerate all child entities within this group. /// - protected abstract IEnumerable AllEntities(); + public abstract IEnumerable AllEntities(); /// public IEnumerable AllElements() => AllEntities().SelectMany(e => e.AllElements()); + /// + /// Whether this entity is directly contained within thie group. + /// + public abstract bool Contains(IMenuEntity entity); + /// /// Register `entity` as a child of this group. /// protected void AddChild(IMenuEntity entity) => entity.SetParents(this, gameObjectParent); + /// + /// Clear all entities from this group. + /// + /// This does not destroy the corresponding game objects. The caller should call `Dispose()` on the entities after `Clear()` if they are no longer wanted. + /// + public abstract void Clear(); + /// /// Enumerate all navigables which should be directly connected in `direction`. /// diff --git a/Elements/BaseSelectableValueElement.cs b/Silksong.ModMenu/Elements/BaseSelectableValueElement.cs similarity index 100% rename from Elements/BaseSelectableValueElement.cs rename to Silksong.ModMenu/Elements/BaseSelectableValueElement.cs diff --git a/Elements/ChoiceElement.cs b/Silksong.ModMenu/Elements/ChoiceElement.cs similarity index 100% rename from Elements/ChoiceElement.cs rename to Silksong.ModMenu/Elements/ChoiceElement.cs diff --git a/Elements/Colors.cs b/Silksong.ModMenu/Elements/Colors.cs similarity index 100% rename from Elements/Colors.cs rename to Silksong.ModMenu/Elements/Colors.cs diff --git a/Elements/DynamicDescriptionChoiceElement.cs b/Silksong.ModMenu/Elements/DynamicDescriptionChoiceElement.cs similarity index 99% rename from Elements/DynamicDescriptionChoiceElement.cs rename to Silksong.ModMenu/Elements/DynamicDescriptionChoiceElement.cs index 9283bc3..ea678ac 100644 --- a/Elements/DynamicDescriptionChoiceElement.cs +++ b/Silksong.ModMenu/Elements/DynamicDescriptionChoiceElement.cs @@ -88,14 +88,14 @@ private static Text SetupRightDescription(Text descriptionText, Text choiceText) return rightText; } - /// + /// public override void SetFontSizes(FontSizes fontSizes) { base.SetFontSizes(fontSizes); RightText.fontSize = fontSizes.DescriptionSize(); } - /// + /// public override void SetMainColor(Color color) { base.SetMainColor(color); diff --git a/Elements/ElementState.cs b/Silksong.ModMenu/Elements/ElementState.cs similarity index 100% rename from Elements/ElementState.cs rename to Silksong.ModMenu/Elements/ElementState.cs diff --git a/Elements/FontSizeConstants.cs b/Silksong.ModMenu/Elements/FontSizeConstants.cs similarity index 100% rename from Elements/FontSizeConstants.cs rename to Silksong.ModMenu/Elements/FontSizeConstants.cs diff --git a/Elements/FontSizes.cs b/Silksong.ModMenu/Elements/FontSizes.cs similarity index 100% rename from Elements/FontSizes.cs rename to Silksong.ModMenu/Elements/FontSizes.cs diff --git a/Elements/FreeGroup.cs b/Silksong.ModMenu/Elements/FreeGroup.cs similarity index 80% rename from Elements/FreeGroup.cs rename to Silksong.ModMenu/Elements/FreeGroup.cs index 52c6816..bac9da0 100644 --- a/Elements/FreeGroup.cs +++ b/Silksong.ModMenu/Elements/FreeGroup.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -18,7 +17,21 @@ public class FreeGroup : AbstractGroup private readonly LinkedDictionary entities = []; /// - protected override IEnumerable AllEntities() => entities.Keys; + public override IEnumerable AllEntities() => entities.Keys; + + /// + /// The total number of entities in this group. + /// + public int Count => entities.Count; + + /// + public override bool Contains(IMenuEntity entity) => entities.ContainsKey(entity); + + /// + /// Gets the positional offset of the given entity, if present. + /// + public bool TryGetOffset(IMenuEntity entity, out Vector2 offset) => + entities.TryGetValue(entity, out offset); /// /// Add an entity to this free group at the specified position. @@ -54,6 +67,15 @@ public bool Remove(IMenuEntity entity) return false; } + /// + public override void Clear() + { + foreach (var entity in entities.Keys) + entity.ClearParents(); + + entities.Clear(); + } + // Sort low values first. private static float SortKey(NavigationDirection direction, Vector2 pos) => direction switch diff --git a/Elements/GridGroup.cs b/Silksong.ModMenu/Elements/GridGroup.cs similarity index 90% rename from Elements/GridGroup.cs rename to Silksong.ModMenu/Elements/GridGroup.cs index bd3263f..52f4b2a 100644 --- a/Elements/GridGroup.cs +++ b/Silksong.ModMenu/Elements/GridGroup.cs @@ -42,6 +42,32 @@ public class GridGroup(int columns) : AbstractGroup /// public bool WrapHorizontal = false; + /// + public override bool Contains(IMenuEntity entity) => index.ContainsKey(entity); + + /// + /// Returns the row and column of the given entity. + /// + public bool TryGetCell(IMenuEntity entity, out int row, out int column) + { + if (index.TryGetValue(entity, out var c)) + { + row = c.Row; + column = c.Column; + return true; + } + + row = -1; + column = -1; + return false; + } + + /// + /// Returns the entity at the given row and column, if present. + /// + public bool TryGetEntity(int row, int column, [MaybeNullWhen(false)] out IMenuEntity entity) => + TryGetValue(new(row, column), out entity); + /// /// Add this entity to the next available empty cell in the grid. /// @@ -112,6 +138,17 @@ public bool Remove(IMenuEntity entity) public bool RemoveAt(int row, int column) => TryGetValue(new(row, column), out var entity) && Remove(entity); + /// + public override void Clear() + { + foreach (var entity in index.Keys) + entity.ClearParents(); + + rows.Clear(); + index.Clear(); + nextEmptyCell = new(0, 0); + } + /// public override bool GetSelectable( NavigationDirection direction, @@ -236,7 +273,7 @@ static bool ClosestColumn( } /// - protected override IEnumerable AllEntities() => + public override IEnumerable AllEntities() => rows.SelectMany(row => row.WhereNonNull()); private ListView> GetColumns() => diff --git a/Elements/IMenuEntity.cs b/Silksong.ModMenu/Elements/IMenuEntity.cs similarity index 100% rename from Elements/IMenuEntity.cs rename to Silksong.ModMenu/Elements/IMenuEntity.cs diff --git a/Elements/IMenuEntityExtensions.cs b/Silksong.ModMenu/Elements/IMenuEntityExtensions.cs similarity index 100% rename from Elements/IMenuEntityExtensions.cs rename to Silksong.ModMenu/Elements/IMenuEntityExtensions.cs diff --git a/Elements/INavigable.cs b/Silksong.ModMenu/Elements/INavigable.cs similarity index 100% rename from Elements/INavigable.cs rename to Silksong.ModMenu/Elements/INavigable.cs diff --git a/Elements/INavigableExtensions.cs b/Silksong.ModMenu/Elements/INavigableExtensions.cs similarity index 100% rename from Elements/INavigableExtensions.cs rename to Silksong.ModMenu/Elements/INavigableExtensions.cs diff --git a/Elements/INavigableMenuEntity.cs b/Silksong.ModMenu/Elements/INavigableMenuEntity.cs similarity index 100% rename from Elements/INavigableMenuEntity.cs rename to Silksong.ModMenu/Elements/INavigableMenuEntity.cs diff --git a/Elements/KeyBindElement.cs b/Silksong.ModMenu/Elements/KeyBindElement.cs similarity index 100% rename from Elements/KeyBindElement.cs rename to Silksong.ModMenu/Elements/KeyBindElement.cs diff --git a/Elements/LocalizedText.cs b/Silksong.ModMenu/Elements/LocalizedText.cs similarity index 100% rename from Elements/LocalizedText.cs rename to Silksong.ModMenu/Elements/LocalizedText.cs diff --git a/Elements/LocalizedTextExtensions.cs b/Silksong.ModMenu/Elements/LocalizedTextExtensions.cs similarity index 100% rename from Elements/LocalizedTextExtensions.cs rename to Silksong.ModMenu/Elements/LocalizedTextExtensions.cs diff --git a/Elements/MenuDisposable.cs b/Silksong.ModMenu/Elements/MenuDisposable.cs similarity index 100% rename from Elements/MenuDisposable.cs rename to Silksong.ModMenu/Elements/MenuDisposable.cs diff --git a/Elements/MenuElement.cs b/Silksong.ModMenu/Elements/MenuElement.cs similarity index 97% rename from Elements/MenuElement.cs rename to Silksong.ModMenu/Elements/MenuElement.cs index 6589aea..5f6e7b3 100644 --- a/Elements/MenuElement.cs +++ b/Silksong.ModMenu/Elements/MenuElement.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Silksong.ModMenu.Internal; using UnityEngine; +using UObject = UnityEngine.Object; namespace Silksong.ModMenu.Elements; @@ -18,6 +19,8 @@ public abstract class MenuElement : MenuDisposable, IMenuEntity protected MenuElement(GameObject container) { Container = container; + OnDispose += () => UObject.Destroy(container); + RectTransform = container.GetComponent(); visibility.OnVisibilityChanged += container.SetActive; diff --git a/Elements/NavigationDirection.cs b/Silksong.ModMenu/Elements/NavigationDirection.cs similarity index 100% rename from Elements/NavigationDirection.cs rename to Silksong.ModMenu/Elements/NavigationDirection.cs diff --git a/Elements/NavigationDirectionExtensions.cs b/Silksong.ModMenu/Elements/NavigationDirectionExtensions.cs similarity index 100% rename from Elements/NavigationDirectionExtensions.cs rename to Silksong.ModMenu/Elements/NavigationDirectionExtensions.cs diff --git a/Elements/SelectableElement.cs b/Silksong.ModMenu/Elements/SelectableElement.cs similarity index 100% rename from Elements/SelectableElement.cs rename to Silksong.ModMenu/Elements/SelectableElement.cs diff --git a/Elements/SelectableValueElement.cs b/Silksong.ModMenu/Elements/SelectableValueElement.cs similarity index 100% rename from Elements/SelectableValueElement.cs rename to Silksong.ModMenu/Elements/SelectableValueElement.cs diff --git a/Elements/SliderElement.cs b/Silksong.ModMenu/Elements/SliderElement.cs similarity index 100% rename from Elements/SliderElement.cs rename to Silksong.ModMenu/Elements/SliderElement.cs diff --git a/Elements/SpacingConstants.cs b/Silksong.ModMenu/Elements/SpacingConstants.cs similarity index 100% rename from Elements/SpacingConstants.cs rename to Silksong.ModMenu/Elements/SpacingConstants.cs diff --git a/Elements/TextButton.cs b/Silksong.ModMenu/Elements/TextButton.cs similarity index 100% rename from Elements/TextButton.cs rename to Silksong.ModMenu/Elements/TextButton.cs diff --git a/Elements/TextInput.cs b/Silksong.ModMenu/Elements/TextInput.cs similarity index 100% rename from Elements/TextInput.cs rename to Silksong.ModMenu/Elements/TextInput.cs diff --git a/Elements/TextLabel.cs b/Silksong.ModMenu/Elements/TextLabel.cs similarity index 100% rename from Elements/TextLabel.cs rename to Silksong.ModMenu/Elements/TextLabel.cs diff --git a/Elements/VerticalGroup.cs b/Silksong.ModMenu/Elements/VerticalGroup.cs similarity index 92% rename from Elements/VerticalGroup.cs rename to Silksong.ModMenu/Elements/VerticalGroup.cs index d67a993..c756f93 100644 --- a/Elements/VerticalGroup.cs +++ b/Silksong.ModMenu/Elements/VerticalGroup.cs @@ -26,7 +26,10 @@ public class VerticalGroup : AbstractGroup public bool HideInactiveElements = true; /// - protected override IEnumerable AllEntities() => entities; + public override IEnumerable AllEntities() => entities; + + /// + public override bool Contains(IMenuEntity entity) => entities.Contains(entity); /// /// Add an entity to this vertical column group. @@ -76,6 +79,15 @@ public void RemoveAt(int index) entity.ClearParents(); } + /// + public override void Clear() + { + foreach (var entity in entities) + entity.ClearParents(); + + entities.Clear(); + } + /// public override bool GetSelectable( NavigationDirection direction, diff --git a/Elements/VisibilityManager.cs b/Silksong.ModMenu/Elements/VisibilityManager.cs similarity index 100% rename from Elements/VisibilityManager.cs rename to Silksong.ModMenu/Elements/VisibilityManager.cs diff --git a/Internal/CollectionsUtil.cs b/Silksong.ModMenu/Internal/CollectionsUtil.cs similarity index 100% rename from Internal/CollectionsUtil.cs rename to Silksong.ModMenu/Internal/CollectionsUtil.cs diff --git a/Internal/CustomInputField.cs b/Silksong.ModMenu/Internal/CustomInputField.cs similarity index 100% rename from Internal/CustomInputField.cs rename to Silksong.ModMenu/Internal/CustomInputField.cs diff --git a/Internal/CustomMappableKey.cs b/Silksong.ModMenu/Internal/CustomMappableKey.cs similarity index 100% rename from Internal/CustomMappableKey.cs rename to Silksong.ModMenu/Internal/CustomMappableKey.cs diff --git a/Internal/CustomMenuOptionHorizontal.cs b/Silksong.ModMenu/Internal/CustomMenuOptionHorizontal.cs similarity index 100% rename from Internal/CustomMenuOptionHorizontal.cs rename to Silksong.ModMenu/Internal/CustomMenuOptionHorizontal.cs diff --git a/Internal/EnumUtil.cs b/Silksong.ModMenu/Internal/EnumUtil.cs similarity index 100% rename from Internal/EnumUtil.cs rename to Silksong.ModMenu/Internal/EnumUtil.cs diff --git a/Internal/EventSuppressor.cs b/Silksong.ModMenu/Internal/EventSuppressor.cs similarity index 100% rename from Internal/EventSuppressor.cs rename to Silksong.ModMenu/Internal/EventSuppressor.cs diff --git a/Internal/EventTriggerUtil.cs b/Silksong.ModMenu/Internal/EventTriggerUtil.cs similarity index 100% rename from Internal/EventTriggerUtil.cs rename to Silksong.ModMenu/Internal/EventTriggerUtil.cs diff --git a/Internal/ExceptionUtil.cs b/Silksong.ModMenu/Internal/ExceptionUtil.cs similarity index 100% rename from Internal/ExceptionUtil.cs rename to Silksong.ModMenu/Internal/ExceptionUtil.cs diff --git a/Internal/GameObjectUtil.cs b/Silksong.ModMenu/Internal/GameObjectUtil.cs similarity index 100% rename from Internal/GameObjectUtil.cs rename to Silksong.ModMenu/Internal/GameObjectUtil.cs diff --git a/Internal/IEnumeratorUtil.cs b/Silksong.ModMenu/Internal/IEnumeratorUtil.cs similarity index 100% rename from Internal/IEnumeratorUtil.cs rename to Silksong.ModMenu/Internal/IEnumeratorUtil.cs diff --git a/Internal/IndexedList.cs b/Silksong.ModMenu/Internal/IndexedList.cs similarity index 100% rename from Internal/IndexedList.cs rename to Silksong.ModMenu/Internal/IndexedList.cs diff --git a/Internal/KeyCodeUtil.cs b/Silksong.ModMenu/Internal/KeyCodeUtil.cs similarity index 100% rename from Internal/KeyCodeUtil.cs rename to Silksong.ModMenu/Internal/KeyCodeUtil.cs diff --git a/Internal/LateUpdateHelper.cs b/Silksong.ModMenu/Internal/LateUpdateHelper.cs similarity index 100% rename from Internal/LateUpdateHelper.cs rename to Silksong.ModMenu/Internal/LateUpdateHelper.cs diff --git a/Internal/LinkedDictionary.cs b/Silksong.ModMenu/Internal/LinkedDictionary.cs similarity index 93% rename from Internal/LinkedDictionary.cs rename to Silksong.ModMenu/Internal/LinkedDictionary.cs index 63a9c2e..f59f170 100644 --- a/Internal/LinkedDictionary.cs +++ b/Silksong.ModMenu/Internal/LinkedDictionary.cs @@ -13,7 +13,7 @@ internal class LinkedDictionary : IDictionary public V this[K key] { get => data[key].Item2; - set => data[key] = (data[key].Item1, value); + set => Set(key, value); } public ICollection Keys => new LinkedDictionaryKeys(this); @@ -24,14 +24,21 @@ public V this[K key] public bool IsReadOnly => false; - public void Add(K key, V value) => + public void Add(K key, V value) + { + if (data.ContainsKey(key)) + throw new ArgumentException($"An element with the same key already exists: {key}"); + data.Add(key, (keyOrder.AddLast(key), value)); + } + + public void Add(KeyValuePair item) => Add(item.Key, item.Value); + + public void Set(K key, V value) => data[key] = ( data.TryGetValue(key, out var pair) ? pair.Item1 : keyOrder.AddLast(key), value ); - public void Add(KeyValuePair item) => Add(item.Key, item.Value); - public void Clear() { data.Clear(); diff --git a/Internal/ListView.cs b/Silksong.ModMenu/Internal/ListView.cs similarity index 100% rename from Internal/ListView.cs rename to Silksong.ModMenu/Internal/ListView.cs diff --git a/Internal/MenuPrefabs.cs b/Silksong.ModMenu/Internal/MenuPrefabs.cs similarity index 100% rename from Internal/MenuPrefabs.cs rename to Silksong.ModMenu/Internal/MenuPrefabs.cs diff --git a/Internal/OnDestroyHelper.cs b/Silksong.ModMenu/Internal/OnDestroyHelper.cs similarity index 100% rename from Internal/OnDestroyHelper.cs rename to Silksong.ModMenu/Internal/OnDestroyHelper.cs diff --git a/Internal/OnSelectHelper.cs b/Silksong.ModMenu/Internal/OnSelectHelper.cs similarity index 100% rename from Internal/OnSelectHelper.cs rename to Silksong.ModMenu/Internal/OnSelectHelper.cs diff --git a/Internal/RectTransformUtil.cs b/Silksong.ModMenu/Internal/RectTransformUtil.cs similarity index 100% rename from Internal/RectTransformUtil.cs rename to Silksong.ModMenu/Internal/RectTransformUtil.cs diff --git a/Internal/SelectableWrapper.cs b/Silksong.ModMenu/Internal/SelectableWrapper.cs similarity index 100% rename from Internal/SelectableWrapper.cs rename to Silksong.ModMenu/Internal/SelectableWrapper.cs diff --git a/Internal/StringUtil.cs b/Silksong.ModMenu/Internal/StringUtil.cs similarity index 100% rename from Internal/StringUtil.cs rename to Silksong.ModMenu/Internal/StringUtil.cs diff --git a/Internal/ThreadLocalContext.cs b/Silksong.ModMenu/Internal/ThreadLocalContext.cs similarity index 100% rename from Internal/ThreadLocalContext.cs rename to Silksong.ModMenu/Internal/ThreadLocalContext.cs diff --git a/Internal/UIManagerUtil.cs b/Silksong.ModMenu/Internal/UIManagerUtil.cs similarity index 100% rename from Internal/UIManagerUtil.cs rename to Silksong.ModMenu/Internal/UIManagerUtil.cs diff --git a/ModMenuPlugin.cs b/Silksong.ModMenu/ModMenuPlugin.cs similarity index 100% rename from ModMenuPlugin.cs rename to Silksong.ModMenu/ModMenuPlugin.cs diff --git a/Models/AbstractValueModel.cs b/Silksong.ModMenu/Models/AbstractValueModel.cs similarity index 100% rename from Models/AbstractValueModel.cs rename to Silksong.ModMenu/Models/AbstractValueModel.cs diff --git a/Models/Attributes.cs b/Silksong.ModMenu/Models/Attributes.cs similarity index 100% rename from Models/Attributes.cs rename to Silksong.ModMenu/Models/Attributes.cs diff --git a/Models/ChoiceModels.cs b/Silksong.ModMenu/Models/ChoiceModels.cs similarity index 100% rename from Models/ChoiceModels.cs rename to Silksong.ModMenu/Models/ChoiceModels.cs diff --git a/Models/Delegates.cs b/Silksong.ModMenu/Models/Delegates.cs similarity index 100% rename from Models/Delegates.cs rename to Silksong.ModMenu/Models/Delegates.cs diff --git a/Models/IBaseChoiceModel.cs b/Silksong.ModMenu/Models/IBaseChoiceModel.cs similarity index 100% rename from Models/IBaseChoiceModel.cs rename to Silksong.ModMenu/Models/IBaseChoiceModel.cs diff --git a/Models/IBaseValueModel.cs b/Silksong.ModMenu/Models/IBaseValueModel.cs similarity index 100% rename from Models/IBaseValueModel.cs rename to Silksong.ModMenu/Models/IBaseValueModel.cs diff --git a/Models/IChoiceModel.cs b/Silksong.ModMenu/Models/IChoiceModel.cs similarity index 100% rename from Models/IChoiceModel.cs rename to Silksong.ModMenu/Models/IChoiceModel.cs diff --git a/Models/IDisplayable.cs b/Silksong.ModMenu/Models/IDisplayable.cs similarity index 100% rename from Models/IDisplayable.cs rename to Silksong.ModMenu/Models/IDisplayable.cs diff --git a/Models/ITextModel.cs b/Silksong.ModMenu/Models/ITextModel.cs similarity index 100% rename from Models/ITextModel.cs rename to Silksong.ModMenu/Models/ITextModel.cs diff --git a/Models/IValueModel.cs b/Silksong.ModMenu/Models/IValueModel.cs similarity index 100% rename from Models/IValueModel.cs rename to Silksong.ModMenu/Models/IValueModel.cs diff --git a/Models/IntRangeChoiceModel.cs b/Silksong.ModMenu/Models/IntRangeChoiceModel.cs similarity index 100% rename from Models/IntRangeChoiceModel.cs rename to Silksong.ModMenu/Models/IntRangeChoiceModel.cs diff --git a/Models/IntSliderModel.cs b/Silksong.ModMenu/Models/IntSliderModel.cs similarity index 100% rename from Models/IntSliderModel.cs rename to Silksong.ModMenu/Models/IntSliderModel.cs diff --git a/Models/LinearFloatSliderModel.cs b/Silksong.ModMenu/Models/LinearFloatSliderModel.cs similarity index 100% rename from Models/LinearFloatSliderModel.cs rename to Silksong.ModMenu/Models/LinearFloatSliderModel.cs diff --git a/Models/ListChoiceModel.cs b/Silksong.ModMenu/Models/ListChoiceModel.cs similarity index 100% rename from Models/ListChoiceModel.cs rename to Silksong.ModMenu/Models/ListChoiceModel.cs diff --git a/Models/ListSliderModel.cs b/Silksong.ModMenu/Models/ListSliderModel.cs similarity index 100% rename from Models/ListSliderModel.cs rename to Silksong.ModMenu/Models/ListSliderModel.cs diff --git a/Models/ParserTextModel.cs b/Silksong.ModMenu/Models/ParserTextModel.cs similarity index 100% rename from Models/ParserTextModel.cs rename to Silksong.ModMenu/Models/ParserTextModel.cs diff --git a/Models/SliderModel.cs b/Silksong.ModMenu/Models/SliderModel.cs similarity index 100% rename from Models/SliderModel.cs rename to Silksong.ModMenu/Models/SliderModel.cs diff --git a/Models/SliderModels.cs b/Silksong.ModMenu/Models/SliderModels.cs similarity index 100% rename from Models/SliderModels.cs rename to Silksong.ModMenu/Models/SliderModels.cs diff --git a/Models/TextModels.cs b/Silksong.ModMenu/Models/TextModels.cs similarity index 100% rename from Models/TextModels.cs rename to Silksong.ModMenu/Models/TextModels.cs diff --git a/Models/ValueModel.cs b/Silksong.ModMenu/Models/ValueModel.cs similarity index 100% rename from Models/ValueModel.cs rename to Silksong.ModMenu/Models/ValueModel.cs diff --git a/Plugin/ConfigEntryFactory.cs b/Silksong.ModMenu/Plugin/ConfigEntryFactory.cs similarity index 100% rename from Plugin/ConfigEntryFactory.cs rename to Silksong.ModMenu/Plugin/ConfigEntryFactory.cs diff --git a/Plugin/IModMenuCustomElement.cs b/Silksong.ModMenu/Plugin/IModMenuCustomElement.cs similarity index 100% rename from Plugin/IModMenuCustomElement.cs rename to Silksong.ModMenu/Plugin/IModMenuCustomElement.cs diff --git a/Plugin/IModMenuCustomMenu.cs b/Silksong.ModMenu/Plugin/IModMenuCustomMenu.cs similarity index 100% rename from Plugin/IModMenuCustomMenu.cs rename to Silksong.ModMenu/Plugin/IModMenuCustomMenu.cs diff --git a/Plugin/IModMenuInterface.cs b/Silksong.ModMenu/Plugin/IModMenuInterface.cs similarity index 100% rename from Plugin/IModMenuInterface.cs rename to Silksong.ModMenu/Plugin/IModMenuInterface.cs diff --git a/Plugin/IModMenuToggle.cs b/Silksong.ModMenu/Plugin/IModMenuToggle.cs similarity index 100% rename from Plugin/IModMenuToggle.cs rename to Silksong.ModMenu/Plugin/IModMenuToggle.cs diff --git a/Plugin/MenuElementGenerators.cs b/Silksong.ModMenu/Plugin/MenuElementGenerators.cs similarity index 100% rename from Plugin/MenuElementGenerators.cs rename to Silksong.ModMenu/Plugin/MenuElementGenerators.cs diff --git a/Plugin/PluginRegistry.cs b/Silksong.ModMenu/Plugin/PluginRegistry.cs similarity index 100% rename from Plugin/PluginRegistry.cs rename to Silksong.ModMenu/Plugin/PluginRegistry.cs diff --git a/README.md b/Silksong.ModMenu/README.md similarity index 100% rename from README.md rename to Silksong.ModMenu/README.md diff --git a/Registry.cs b/Silksong.ModMenu/Registry.cs similarity index 100% rename from Registry.cs rename to Silksong.ModMenu/Registry.cs diff --git a/Screens/AbstractMenuScreen.cs b/Silksong.ModMenu/Screens/AbstractMenuScreen.cs similarity index 100% rename from Screens/AbstractMenuScreen.cs rename to Silksong.ModMenu/Screens/AbstractMenuScreen.cs diff --git a/Screens/BasicMenuScreen.cs b/Silksong.ModMenu/Screens/BasicMenuScreen.cs similarity index 100% rename from Screens/BasicMenuScreen.cs rename to Silksong.ModMenu/Screens/BasicMenuScreen.cs diff --git a/Screens/HistoryMode.cs b/Silksong.ModMenu/Screens/HistoryMode.cs similarity index 100% rename from Screens/HistoryMode.cs rename to Silksong.ModMenu/Screens/HistoryMode.cs diff --git a/Screens/MenuScreenNavigation.cs b/Silksong.ModMenu/Screens/MenuScreenNavigation.cs similarity index 100% rename from Screens/MenuScreenNavigation.cs rename to Silksong.ModMenu/Screens/MenuScreenNavigation.cs diff --git a/Screens/PaginatedMenuScreen.cs b/Silksong.ModMenu/Screens/PaginatedMenuScreen.cs similarity index 100% rename from Screens/PaginatedMenuScreen.cs rename to Silksong.ModMenu/Screens/PaginatedMenuScreen.cs diff --git a/Screens/PaginatedMenuScreenBuilder.cs b/Silksong.ModMenu/Screens/PaginatedMenuScreenBuilder.cs similarity index 100% rename from Screens/PaginatedMenuScreenBuilder.cs rename to Silksong.ModMenu/Screens/PaginatedMenuScreenBuilder.cs diff --git a/Screens/SelectOnShowBehaviour.cs b/Silksong.ModMenu/Screens/SelectOnShowBehaviour.cs similarity index 100% rename from Screens/SelectOnShowBehaviour.cs rename to Silksong.ModMenu/Screens/SelectOnShowBehaviour.cs diff --git a/Screens/SimpleMenuScreen.cs b/Silksong.ModMenu/Screens/SimpleMenuScreen.cs similarity index 100% rename from Screens/SimpleMenuScreen.cs rename to Silksong.ModMenu/Screens/SimpleMenuScreen.cs diff --git a/Silksong.ModMenu.csproj b/Silksong.ModMenu/Silksong.ModMenu.csproj similarity index 94% rename from Silksong.ModMenu.csproj rename to Silksong.ModMenu/Silksong.ModMenu.csproj index 384d604..2e9fbd8 100644 --- a/Silksong.ModMenu.csproj +++ b/Silksong.ModMenu/Silksong.ModMenu.csproj @@ -3,7 +3,7 @@ Imports silksong path properties only if present in order to allow CI builds. The file should be gitignored. If you are checking out from git and need to create a new one, you can use `dotnet new silksongpath` to generate one. --> - + netstandard2.1 latest @@ -64,9 +64,8 @@ - - + @@ -75,7 +74,7 @@ - $(ProjectDir)/thunderstore + $(SolutionDir)/thunderstore diff --git a/Util/SelectableUtil.cs b/Silksong.ModMenu/Util/SelectableUtil.cs similarity index 100% rename from Util/SelectableUtil.cs rename to Silksong.ModMenu/Util/SelectableUtil.cs diff --git a/Silksong.ModMenuTesting/Directory.Build.props b/Silksong.ModMenuTesting/Directory.Build.props new file mode 100644 index 0000000..e898a36 --- /dev/null +++ b/Silksong.ModMenuTesting/Directory.Build.props @@ -0,0 +1,16 @@ + + + + + ModMenuTesting + + 0.1.0 + + diff --git a/Silksong.ModMenuTesting/ModMenuTest.cs b/Silksong.ModMenuTesting/ModMenuTest.cs new file mode 100644 index 0000000..cc0972e --- /dev/null +++ b/Silksong.ModMenuTesting/ModMenuTest.cs @@ -0,0 +1,10 @@ +using Silksong.ModMenu.Screens; + +namespace Silksong.ModMenuTesting; + +internal abstract class ModMenuTest +{ + internal abstract string Name { get; } + + internal abstract AbstractMenuScreen BuildMenuScreen(); +} diff --git a/Silksong.ModMenuTesting/ModMenuTestingPlugin.cs b/Silksong.ModMenuTesting/ModMenuTestingPlugin.cs new file mode 100644 index 0000000..5017188 --- /dev/null +++ b/Silksong.ModMenuTesting/ModMenuTestingPlugin.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using BepInEx; +using Silksong.ModMenu.Elements; +using Silksong.ModMenu.Plugin; +using Silksong.ModMenu.Screens; + +namespace Silksong.ModMenuTesting; + +[BepInAutoPlugin(id: "org.silksong_modding.modmenutesting")] +public partial class ModMenuTestingPlugin : BaseUnityPlugin, IModMenuCustomMenu +{ + private void Awake() + { + // Put your initialization logic here + Logger.LogInfo($"Plugin {Name} ({Id}) has loaded!"); + } + + private static IEnumerable CreateTests() + { + foreach ( + var type in Assembly + .GetExecutingAssembly() + .GetTypes() + .Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(ModMenuTest))) + ) + yield return (ModMenuTest)Activator.CreateInstance(type); + } + + public AbstractMenuScreen BuildCustomMenu() + { + PaginatedMenuScreenBuilder builder = new("Mod Menu Testing"); + foreach (var test in CreateTests().OrderBy(t => t.Name)) + { + TextButton button = new(test.Name); + + List screen = []; + var testCopy = test; + button.OnSubmit += () => + { + if (screen.Count == 0) + { + try + { + screen.Add(testCopy.BuildMenuScreen()); + } + catch (Exception ex) + { + button.State = ElementState.INVALID; + UnityEngine.Debug.LogError( + $"Failed to build mod menu for '{testCopy.Name}': {ex}" + ); + } + } + + if (screen.Count > 0) + MenuScreenNavigation.Show(screen[0]); + }; + + builder.Add(button); + } + + return builder.Build(); + } + + public string ModMenuName() => "Mod Menu Testing"; +} diff --git a/Silksong.ModMenuTesting/README.md b/Silksong.ModMenuTesting/README.md new file mode 100644 index 0000000..d2deb5c --- /dev/null +++ b/Silksong.ModMenuTesting/README.md @@ -0,0 +1,3 @@ +# Silksong.ModMenuTesting + +Testing plugin for Silksong.ModMenu. Provides examples of the many library features. diff --git a/Silksong.ModMenuTesting/Silksong.ModMenuTesting.csproj b/Silksong.ModMenuTesting/Silksong.ModMenuTesting.csproj new file mode 100644 index 0000000..464803c --- /dev/null +++ b/Silksong.ModMenuTesting/Silksong.ModMenuTesting.csproj @@ -0,0 +1,47 @@ + + + + + netstandard2.1 + latest + enable + True + recommended + + $(NoWarn);MSB3270 + + + $(MSBuildProjectDirectory)=/ + + false + https://github.com/silksong_modding/Silksong.ModMenuTesting + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Silksong.ModMenuTesting/Tests/FreeGroupTest.cs b/Silksong.ModMenuTesting/Tests/FreeGroupTest.cs new file mode 100644 index 0000000..3432755 --- /dev/null +++ b/Silksong.ModMenuTesting/Tests/FreeGroupTest.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using Silksong.ModMenu.Elements; +using Silksong.ModMenu.Screens; + +namespace Silksong.ModMenuTesting.Tests; + +internal class FreeGroupTest : ModMenuTest +{ + internal override string Name => "Free Group"; + + internal override AbstractMenuScreen BuildMenuScreen() + { + FreeGroup group = new(); + + TextButton topLeft = new("Top Left"); + group.Add(topLeft, new(-800, 0)); + + List toggled = [true]; + TextButton toggleTopLeft = new("Toggle Top Left"); + toggleTopLeft.OnSubmit += () => + { + toggled[0] = !toggled[0]; + if (toggled[0]) + group.Add(topLeft, new(-800, 0)); + else + group.Remove(topLeft); + }; + group.Add(toggleTopLeft, new(-800, -200)); + + TextButton middle = new("Middle"); + group.Add(middle, new(0, -400)); + + TextButton moveLeft = new("Move Middle Left"); + moveLeft.OnSubmit += () => + { + group.TryGetOffset(middle, out var offset); + offset.x -= 25; + group.Update(middle, offset); + }; + group.Add(moveLeft, new(-800, -600)); + + TextButton moveRight = new("Move Middle Right"); + moveRight.OnSubmit += () => + { + group.TryGetOffset(middle, out var offset); + offset.x += 25; + group.Update(middle, offset); + }; + group.Add(moveRight, new(800, -600)); + + return new BasicMenuScreen("Free Group Test", group); + } +} diff --git a/Silksong.ModMenuTesting/Tests/GridGroupTest.cs b/Silksong.ModMenuTesting/Tests/GridGroupTest.cs new file mode 100644 index 0000000..1b42b0d --- /dev/null +++ b/Silksong.ModMenuTesting/Tests/GridGroupTest.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Silksong.ModMenu.Elements; +using Silksong.ModMenu.Models; +using Silksong.ModMenu.Screens; + +namespace Silksong.ModMenuTesting.Tests; + +internal class GridGroupTest : ModMenuTest +{ + private enum Spacing + { + Small, + Medium, + Large, + } + + internal override string Name => "Grid Group"; + + internal override AbstractMenuScreen BuildMenuScreen() + { + GridGroup group = new(2); + + group.Add(new TextButton("Button 1")); + group.Add(new TextButton("Button 2")); + + ChoiceElement hSpacing = new( + "Horizontal Spacing", + ChoiceModels.ForEnum() + ); + hSpacing.OnValueChanged += value => + group.HorizontalSpacing = value switch + { + Spacing.Small => SpacingConstants.HSPACE_SMALL, + Spacing.Medium => SpacingConstants.HSPACE_MEDIUM, + Spacing.Large => SpacingConstants.HSPACE_LARGE, + _ => throw new ArgumentException($"{value}"), + }; + hSpacing.Value = Spacing.Medium; + group.HorizontalSpacing = SpacingConstants.HSPACE_MEDIUM; + group.Add(hSpacing); + group.Add(new TextButton("")); + + ChoiceElement vSpacing = new("Vertical Spacing", ChoiceModels.ForEnum()); + vSpacing.OnValueChanged += value => + group.VerticalSpacing = value switch + { + Spacing.Small => SpacingConstants.VSPACE_SMALL, + Spacing.Medium => SpacingConstants.VSPACE_MEDIUM, + Spacing.Large => SpacingConstants.VSPACE_LARGE, + _ => throw new ArgumentException($"{value}"), + }; + vSpacing.Value = Spacing.Medium; + group.VerticalSpacing = SpacingConstants.VSPACE_MEDIUM; + group.Add(vSpacing); + group.Add(new TextButton("")); + + Stack addedButtons = []; + + TextButton addButton = new("Add"); + addButton.OnSubmit += () => + { + TextButton button = new("New Button"); + group.Add(button); + addedButtons.Push(button); + }; + group.Add(addButton); + + TextButton removeButton = new("Remove"); + removeButton.OnSubmit += () => + { + if (addedButtons.Count > 0) + { + TextButton button = addedButtons.Pop(); + group.Remove(button); + button.Dispose(); + } + }; + group.Add(removeButton); + + return new BasicMenuScreen("Grid Group Test", group); + } +} diff --git a/Silksong.ModMenuTesting/Tests/VerticalGroupTest.cs b/Silksong.ModMenuTesting/Tests/VerticalGroupTest.cs new file mode 100644 index 0000000..535f44c --- /dev/null +++ b/Silksong.ModMenuTesting/Tests/VerticalGroupTest.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using Silksong.ModMenu.Elements; +using Silksong.ModMenu.Models; +using Silksong.ModMenu.Screens; + +namespace Silksong.ModMenuTesting.Tests; + +internal enum VerticalGroupSpacing +{ + Small, + Medium, + Large, +} + +internal class VerticalGroupTest : ModMenuTest +{ + internal override string Name => "Vertical Group"; + + internal override AbstractMenuScreen BuildMenuScreen() + { + VerticalGroup group = new(); + + TextButton resetButton = new("Reset"); + void Reset() + { + foreach (var element in group.AllElements()) + if (element != resetButton) + element.Dispose(); + group.Clear(); + + ChoiceElement spacing = new( + "Spacing", + ChoiceModels.ForEnum() + ); + spacing.OnValueChanged += value => + group.VerticalSpacing = value switch + { + VerticalGroupSpacing.Small => SpacingConstants.VSPACE_SMALL, + VerticalGroupSpacing.Medium => SpacingConstants.VSPACE_MEDIUM, + VerticalGroupSpacing.Large => SpacingConstants.VSPACE_LARGE, + _ => throw new ArgumentException($"{value}"), + }; + spacing.Value = VerticalGroupSpacing.Medium; + group.VerticalSpacing = SpacingConstants.VSPACE_MEDIUM; + group.Add(spacing); + + TextButton insertBelow = new("Insert Below"); + insertBelow.OnSubmit += () => group.Insert(2, new TextButton("New Button")); + group.Add(insertBelow); + + TextButton removeThird = new("Remove Third Button"); + removeThird.OnSubmit += () => group.RemoveAt(2); + group.Add(removeThird); + + TextButton maybeVisible = new("Maybe Visible"); + group.Add(maybeVisible); + + ChoiceElement isAboveVisible = new("Is Above Visible?", ChoiceModels.ForBool()); + isAboveVisible.OnValueChanged += value => maybeVisible.VisibleSelf = value; + isAboveVisible.Value = true; + group.Add(isAboveVisible); + + ChoiceElement hideGaps = new("Hide Gaps", ChoiceModels.ForBool()); + hideGaps.OnValueChanged += value => group.HideInactiveElements = value; + group.HideInactiveElements = false; + hideGaps.Value = false; + group.Add(hideGaps); + + group.Add(resetButton); + } + resetButton.OnSubmit += Reset; + Reset(); + + return new BasicMenuScreen("Vertical Group Test", group); + } +} diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..ec22c2a --- /dev/null +++ b/nuget.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/thunderstore/thunderstore.toml b/thunderstore/thunderstore.toml index 7b6bcb1..c29c6af 100644 --- a/thunderstore/thunderstore.toml +++ b/thunderstore/thunderstore.toml @@ -21,7 +21,7 @@ silksong_modding-UnityHelper = "1.0.1" # by other build scripts [build] icon = "./icon.png" -readme = "../README.md" +readme = "../Silksong.ModMenu/README.md" outdir = "./dist" [[build.copy]]