Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
13 changes: 13 additions & 0 deletions Silksong.ModMenu/Elements/TextButton.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using Silksong.ModMenu.Internal;
using Silksong.ModMenu.Screens;
using Silksong.UnityHelper.Extensions;
using UnityEngine;
using UnityEngine.EventSystems;
Expand Down Expand Up @@ -45,6 +46,18 @@ public TextButton(LocalizedText text, LocalizedText description)
public TextButton(LocalizedText text)
: this(text, string.Empty) { }

/// <summary>
/// Construct a text button that jumps to the given menu screen on click.
///
/// The text button will have the same text as the linked screen's title.
/// </summary>
/// <param name="screen"></param>
public TextButton(AbstractMenuScreen screen)
: this(screen.TitleText.LocalizedText)
{
OnSubmit = () => MenuScreenNavigation.Show(screen);
}

/// <summary>
/// The action(s) to perform when this button is selected.
/// This takes place on the UI Thread and so must be relatively instantaneous.
Expand Down
19 changes: 19 additions & 0 deletions Silksong.ModMenu/Models/Attributes.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Reflection;
using Silksong.ModMenu.Plugin;

namespace Silksong.ModMenu.Models;

Expand Down Expand Up @@ -31,3 +32,21 @@ internal static bool IgnoreForModMenu(this MemberInfo self) =>
|| self.GetCustomAttributes(true)
.Any(attr => attr.GetType().Name == nameof(ModMenuIgnoreAttribute));
}

/// <summary>
/// Attribute added to an enum member to indicate how its description should be localized,
/// for use in <see cref="MenuElementGenerators.CreateRightDescGenerator"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class LocalizedDescriptionAttribute(string sheetTitle, string key) : Attribute
{
/// <summary>
/// The sheet title for the localized description.
/// </summary>
public string SheetTitle => sheetTitle;

/// <summary>
/// The key for the localized description.
/// </summary>
public string Key => key;
}
25 changes: 23 additions & 2 deletions Silksong.ModMenu/Plugin/ConfigEntryFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,29 @@ TreeNode<LocalizedText, ElementTreeNode> tree
subpageNames.RemoveRange(origSize, subpageNames.Count - origSize);
}

PaginatedMenuScreenBuilder builder = new(subpageNames.LastOrDefault() ?? menuName);
builder.AddRange(elements.OrderBy(e => e.path).Select(e => e.element));
return ArrangeScreen(
elements.OrderBy(e => e.path).ToList(),
subpageNames.LastOrDefault() ?? menuName
);
}

/// <summary>
/// Given a list of menu elements, create a menu screen for those elements.
///
/// This function is used by the default implementation of
/// <see cref="GenerateEntryButton(LocalizedText, BaseUnityPlugin, out SelectableElement)"/>
/// to build each subpage.
/// </summary>
/// <param name="elements">Pairs (path to element, element) to arrange.</param>
/// <param name="menuName">The title of the menu.</param>
/// <returns>A menu screen containing those elements.</returns>
protected virtual AbstractMenuScreen ArrangeScreen(
List<(string path, MenuElement element)> elements,
LocalizedText menuName
)
{
PaginatedMenuScreenBuilder builder = new(menuName);
builder.AddRange(elements.Select(e => e.element));
return builder.Build();
}

Expand Down
41 changes: 33 additions & 8 deletions Silksong.ModMenu/Plugin/MenuElementGenerators.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
Expand All @@ -15,9 +16,10 @@ namespace Silksong.ModMenu.Plugin;
public static class MenuElementGenerators
{
/// <summary>
/// Returns a Generator that creates a choice element with a description below the option value.
/// Returns a generator that creates a choice element with a description below the option value for an enum option.
///
/// The description will be taken from the <see cref="DescriptionAttribute"/> attributes on the enum members.
/// The description will be taken from the <see cref="LocalizedDescriptionAttribute"/> or
/// <see cref="DescriptionAttribute"/> attributes on the enum members.
/// </summary>
/// <param name="includeSettingDescription">If true, will also include the default description below the setting name.</param>
public static ConfigEntryFactory.MenuElementGenerator CreateRightDescGenerator(
Expand All @@ -43,9 +45,9 @@ out ListChoiceModel<object>? model
return false;
}

Dictionary<object, string> descriptionLookup = model.Values.ToDictionary(
Dictionary<object, LocalizedText> descriptionLookup = model.Values.ToDictionary(
t => t,
t => string.Empty
t => LocalizedText.Raw(string.Empty)
);
bool anyDesc = false;
foreach (object member in model.Values)
Expand All @@ -54,10 +56,10 @@ out ListChoiceModel<object>? model
member.ToString(),
BindingFlags.Static | BindingFlags.Public
);
DescriptionAttribute? desc = enumField.GetCustomAttribute<DescriptionAttribute>();
if (desc is not null)

if (TryGetDescription(enumField, out LocalizedText? descText))
{
descriptionLookup[member] = desc.Description;
descriptionLookup[member] = descText;
anyDesc = true;
}
}
Expand Down Expand Up @@ -85,6 +87,29 @@ out ListChoiceModel<object>? model
return gen;
}

private static bool TryGetDescription(
FieldInfo enumField,
[NotNullWhen(true)] out LocalizedText? descText
)
{
LocalizedDescriptionAttribute lDesc =
enumField.GetCustomAttribute<LocalizedDescriptionAttribute>();
if (lDesc is not null)
{
descText = LocalizedText.Key(new(lDesc.SheetTitle, lDesc.Key));
return true;
}

DescriptionAttribute? desc = enumField.GetCustomAttribute<DescriptionAttribute>();
if (desc is not null)
{
descText = desc.Description;
return true;
}
descText = default;
return false;
}

/// <summary>
/// Create a generator that converts an int-valued <see cref="ConfigEntry{T}"/>
/// with a AcceptableValueRange to a slider element.
Expand Down